Ao usar awk / pattern / {print “text”} / patern / {print “”}, existe um padrão ELSE?

22

Digamos que eu tenha um arquivo de texto como:

R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

Eu quero usar awkpara processar essas linhas de maneira diferente, como

awk '/R1/ { print "=>" $0} /R2/ { print "*" $0} '

e também quero imprimir o restante das linhas como estão (sem fazer duplicatas das linhas que já processei), basicamente preciso de uma /ELSE/ { print $0}no final da minha awklinha.

Existe uma coisa dessas?

Todos
fonte

Respostas:

27

Abordagem simplificada com awk

awk '/R1/ {print "=>" $0;next} /R2/{print "*" $0;next} 1' text.file

[jaypal:~/Temp] cat text.file 
R1 12 324 3453 36 457 4 7 8
R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242

[jaypal:~/Temp] awk '/R1/ { print "=>" $0;next} /R2/{print "*" $0;next}1' text.file
=>R1 12 324 3453 36 457 4 7 8
*R2 34 2342 2525 25 25 26 26 2 2
R3 23 2342 32 52 54 543 643 63
R4 25 234 2342 4 234242
[jaypal:~/Temp] 

Violação das Instruções Padrão {Ação}:

  • /R1/ { print "=>" $0;next}: Isso significa que as linhas com /R1/a ação de impressão =>serão executadas. nextsignifica que o restante das instruções awk será ignorado e a próxima linha será analisada.

  • /R2/{print "*" $0;next}: Isso significa que as linhas correspondentes pattern /R2/à ação de impressão *serão executadas. Quando o awkprocessamento é iniciado, a primeira pattern {action}instrução será ignorada, pois pattern /R1/não será verdadeira para as linhas /R2/. Então, a segunda pattern {action}declaração será feita na linha. nextsignificaria novamente que não queremos mais processamento e awkiremos devidamente para a próxima linha.

  • 1imprime todas as linhas. Quando apenas uma condição é fornecida sem {action}, o awk usa como padrão {print}. Aqui a condição é 1interpretada como verdadeira, portanto sempre é bem-sucedida. Se chegarmos a esse ponto, é porque a primeira e a segunda pattern {action}instruções foram ignoradas ou ignoradas (para linhas que não contêm /R1/e /R2/); portanto, a ação de impressão padrão será executada para as linhas restantes.

jaypal singh
fonte
Parece executar marginalmente o mais rápido de todas as soluções postadas.
Chris Baixo
1
Não tenho certeza se o açúcar sintático é o termo certo aqui ... É apenas sintaxe.
Daniel Hershcovich 30/11
7

awkimplementa os suspeitos usuais quando se trata de condicionais. É uma boa ideia usar no printflugar do printtrabalho que você deseja fazer na partida.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'
Chris Down
fonte
Você realmente não precisa if-then-elsedisso.
Jaypal singh
1
Embora isso funcione perfeitamente bem, não é idiomático. O uso criterioso de nexté uma ferramenta importante na programação do awk.
precisa
2
Eu não entendo o ponto de usar printfaqui. Sua única vantagem (a menos que você esteja fazendo uma formatação mais sofisticada que a concatenação) é que ela não adiciona uma nova linha, que não é relevante aqui.
Gilles 'SO- stop be evil' (
1
Esse é um resultado contra-intuitivo e surpreendente. Não adornado printapenas tem que ser produzido, $0enquanto que printfé necessário analisar uma sequência de formato.
Jw013
5

Chris Down já mostrou como você pode obter outra coisa para regexps usando uma declaração explícita 'if' em um bloco. Você também pode obter o mesmo efeito de outras maneiras, embora a solução dele seja provavelmente melhor.

Uma é escrever um terceiro regex que corresponda apenas ao texto não correspondido pelos outros, no seu caso, isso seria algo como isto:

awk '/^R1/ { print "=>" $0}
     /^R2/ { print "*" $0}
     /^[^R]/ || /^R[^12]/ { print $0 } '

Observe que isso usa regexps ancorados - o ^ no início dos regexps corresponde apenas ao início de uma linha - seus padrões originais não fizeram isso, o que diminui ligeiramente a correspondência, pois verifica todos os caracteres de uma linha em vez de pulando até a próxima linha. O terceiro caso ("else") corresponderá a uma linha que começa com um caractere que não é 'R' ([^ R]) ou que começa com um 'R' seguido por um caractere que não é um '1' ou ' 2 '(R [^ 12]). Os dois significados diferentes de ^ são um tanto confusos, mas esse erro foi cometido há muito tempo e não será alterado tão cedo.

Para usar regexps complementares, eles realmente precisam ser ancorados; caso contrário, o [^ R] corresponderia, por exemplo, ao 1 seguinte. Para regexps muito simples como você, essa abordagem pode ser útil, mas à medida que os regexps se tornam mais complexos, essa abordagem se torna incontrolável. Em vez disso, você pode usar variáveis ​​de estado para cada linha, assim:

awk '{ handled = 0 }
     /^R1/ { print "=>" $0; handled = 1}
     /^R2/ { print "*" $0; handled = 1}
     { if (!handled) print $0 } '

Isso define manipulado como zero para cada nova linha e, em seguida, como 1 se corresponder a um dos dois regexps e, finalmente, se ainda for zero, executa a impressão $ 0.

Alex Dupuy
fonte
Note-se que em arquivos grandes ambos são menos eficientes do que o uso de condicionais (como mostrado aqui ). rfilesão apenas 10.000 linhas do conjunto de dados do questionador repetidas.
Chris Baixo
4
if (!handled)Que nojo! Use nextpara parar de considerar outras ações.
precisa
+1 para if (!handled). Soluções gerais, flexíveis e reutilizáveis ​​são boas. E se a próxima pessoa que tiver essa pergunta quiser fazer mais processamento após a impressão? As respostas com nextnão suportam isso.
Scott