Extraia linhas entre e incluindo 2 padrões

4

Eu tenho um arquivo com dados estruturados da seguinte forma

1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
2, q1, q2, q3, q4, q5, q6, q7
2, q9, q10,,
2, r1, r2, r3, r4, r5, r6, r7
2, r9, r10,,
1, s1, s2, s3, s4, s5, s6, s7
2, s9, s10,,
...

Eu quero começar todas as linhas começando com 1 e terminando com , para que eu obtenha

1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
1, s1, s2, s3, s4, s5, s6, s7
2, s9, s10,,

e, se possível, faça como

1, p1, p2, p3, p4, p5, p6, p7, 2, p9, p10,,
1, s1, s2, s3, s4, s5, s6, s7, 2, s9, s10,,

Como posso fazer isso com sed ou awk?

    
por jasmaar 30.12.2014 / 16:40

3 respostas

2

Aqui está uma solução sed que irá juntar qualquer seqüência das linhas ,,$ após a última ^1 line:

sed -e '/^1/{x;s/\n/ /gp;d' -e '};/,,$/H;$G;D
' <<\IN                                                                          
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
2, q1, q2, q3, q4, q5, q6, q7
2, q9, q10,,
2, r1, r2, r3, r4, r5, r6, r7
2, r9, r10,,
1, s1, s2, s3, s4, s5, s6, s7
2, s9, s10,,
IN

Ele e x alteram h espaços antigos e padrões nas linhas que começam com ^1 e s///p rints, independentemente do conteúdo do h old space anterior àquele no caso de um êxito substituição. As linhas que terminam com ,,$ são anexadas a H old space após um caractere \n ewline e, em seguida, todas as linhas são D eletidas até o primeiro caractere \n ewline. No espaço $ última linha H antigo é anexado ao espaço padrão após um \n ewline - assim, quando é D eleted, ele reinicia o ciclo de linha na parte superior do script contendo apenas o que H old espaço contido - que é impresso conforme necessário.

OUTPUT:

1, p1, p2, p3, p4, p5, p6, p7 2, p9, p10,, 2, q9, q10,, 2, r9, r10,,
1, s1, s2, s3, s4, s5, s6, s7 2, s9, s10,,

Se, por outro lado, você não quiser as ocorrências /,,$/ subseqüentes, isso poderá acontecer:

sed -e '/^1/{x;y/\n/ /;s/,,.*/,,/p;d' -e '};/,,$/H;$G;D'

Com base na mesma entrada que é impressa:

1, p1, p2, p3, p4, p5, p6, p7 2, p9, p10,,
1, s1, s2, s3, s4, s5, s6, s7 2, s9, s10,,

Mas isso fará com que /,,$/ linhas sejam impressas, mesmo que elas não sigam imediatamente uma correspondência /^1/ . Se você quer os pares apenas se eles são imediatamente sequenciais na entrada - você pode fazer isso também:

sed -n '/^1/!d;$p;N;/\n1/P;/,,$/s/\n/ /p;D'

Isso funciona assim:

  • Primeiro d elimina da saída todas as linhas que ! não começam com /^1/
    • Isso inclui as linhas trazidas com N que não terminam com /,,$/ .
  • Se o espaço de padrão da última linha de entrada $ for o espaço aqui p rinted, porque o próximo comando encerrará o script.
  • Em /^1/ corresponde anexa a linha de entrada N ext ao espaço padrão seguindo um caractere \n ewline.
  • Se a linha anexada também começar com /\n1/ it P rints a anterior.
    • P imprime apenas até o primeiro \n ewline no espaço padrão.
  • Depois de extrair a linha de entrada N ext se o espaço de padrão $ terminar com uma correspondência /,,$/ , s/// ubstittará um caractere de espaço para o caractere \n ewline inserido e p rints os resultados.
  • O espaço padrão é sempre D eletido até e incluindo o primeiro caractere \n ewline que ocorre.
    • ... assim, quando uma linha de entrada N ext não corresponde a /,,$/ , ela é enviada de volta para o início do script como o cabeçalho da linha. Se, nesse ponto, ele não corresponder a ^1 , ele será d totalmente eliminado.
    • ... porque /,,$/ já tiveram seu \n ewline completamente removido por este ponto, eles são removidos do fluxo completamente aqui.

Tudo isso significa que se /^1/ linhas se seguirem, elas ainda serão impressas e se as linhas que não terminarem em ,,$ seguirem um ^1 , elas não serão impressas.

    
por 30.12.2014 / 17:14
2

Em relação à sua primeira consulta, você pode usar -e para combinar a expressão em sed:

~$ sed -n -e '/^1/p' -e '/,,$/p' f
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
2, p9, p10,,
2, p9, p10,,
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,

e se você não quiser a duplicata:

~$ sed -n -e '/^1/p' -e '/,,$/p' f | uniq
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,

Com o awk, combine o regex com ; :

~$ awk '/^1/;/,,$/' f
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
2, p9, p10,,
2, p9, p10,,
1, p1, p2, p3, p4, p5, p6, p7
2, p9, p10,,
    
por 30.12.2014 / 16:44
1

Você pode definir condicionalmente ORS para obter a saída no formato desejado

awk '/^1/,/,,$/{ORS = /^1/? ", ": "\n"; print}' file
1, p1, p2, p3, p4, p5, p6, p7, 2, p9, p10,,
1, p1, p2, p3, p4, p5, p6, p7, 2, p9, p10,,
    
por 30.12.2014 / 16:51

Tags