Cuando se usa awk / pattern / {print "text"} / patern / {print ""} ¿hay algún otro patrón?

22

Digamos que tengo un archivo 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

Quiero usar awkpara procesar estas líneas de manera diferente, como

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

y también quiero imprimir el resto de las líneas tal como están (sin hacer duplicados de las líneas que ya he procesado), básicamente necesito un /ELSE/ { print $0}al final de mi awklínea.

¿Hay tal cosa?

Ali
fuente

Respuestas:

27

Enfoque simplificado con 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] 

Desglose de las declaraciones del patrón {Acción}:

  • /R1/ { print "=>" $0;next}: Esto significa que se realizarán líneas que tengan /R1/la acción de imprimir =>. nextsignifica que el resto de las declaraciones awk serán ignoradas y se verá la siguiente línea.

  • /R2/{print "*" $0;next}: Esto significa que se realizarán líneas que coincidan con pattern /R2/la acción de impresión *. Cuando awkcomienza el procesamiento, la primera pattern {action}instrucción se ignorará ya pattern /R1/que no será cierto para las líneas que tienen /R2/. Entonces la segunda pattern {action}declaración se hará en la línea. nextsignificaría nuevamente que no queremos más procesamiento y que pasaremos awka la siguiente línea.

  • 1Imprime todas las líneas. Cuando solo se suministra una condición con no {action}, awk usa de manera predeterminada {print}. Aquí la condición es la 1que se interpreta como verdadera, por lo que siempre tiene éxito. Si llegamos a este punto, es porque las pattern {action}declaraciones primera y segunda fueron ignoradas o pasadas por alto (para las líneas que no contienen /R1/y /R2/), por lo que la acción de impresión predeterminada se realizará para las líneas restantes.

jaypal singh
fuente
Parece ejecutar marginalmente el más rápido de todas las soluciones publicadas.
Chris Down
1
No estoy seguro de que el azúcar sintáctico sea ​​el término correcto aquí ... Es solo sintaxis.
Daniel Hershcovich
7

awkimplementa los sospechosos habituales cuando se trata de condicionales. Es una buena idea utilizarlo en printflugar de printpara el trabajo que desea hacer en el partido.

awk '{ if (/^R1/) { printf("=> %s\n", $0) } else if (/^R2/) { printf("* %s\n", $0) } else { print $0 } }'
Chris Down
fuente
Realmente no necesitas if-then-elsepara esto.
jaypal singh
1
Si bien esto funciona perfectamente bien, no es idiomático. El uso juicioso de nextes una herramienta importante en la programación de awk.
dmckee
2
No entiendo el punto de usar printfaquí. Su única ventaja (a menos que esté haciendo un formato más elegante que la concatenación) es que no agrega una nueva línea, lo que no es relevante aquí.
Gilles 'SO- deja de ser malvado'
1
Ese es un resultado contradictorio y sorprendente. Sin adornos printsolo tiene que salir $0mientras que printftiene que analizar una cadena de formato.
jw013
5

Chris Down ya mostró cómo puede obtener un else para expresiones regulares mediante el uso de una declaración explícita 'if' en un bloque. También puede obtener el mismo efecto de otras maneras, aunque su solución es probablemente mejor.

Una es escribir una tercera expresión regular que solo coincida con el texto que no coincida con los demás, en su caso, esto se vería así:

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

Tenga en cuenta que esto usa expresiones regulares ancladas: la ^ al comienzo de las expresiones regulares solo coincidirá al comienzo de una línea; sus patrones originales no hicieron esto, lo que ralentiza ligeramente la coincidencia ya que verificará todos los caracteres en una línea en lugar de saltando hasta la siguiente línea. El tercer caso ("else") coincidirá con una línea que comienza con algún carácter que no es 'R' ([^ R]) o que comienza con una 'R' seguida de un carácter que no es '1' o ' 2 '(R [^ 12]). Los dos significados diferentes de ^ son algo confusos, pero ese error se cometió hace mucho tiempo y no se cambiará pronto.

Para usar expresiones regulares complementarias, realmente necesitan estar ancladas, ya que de lo contrario el [^ R] coincidiría, por ejemplo, con el 1 siguiente. Para expresiones regulares muy simples como las que tiene, este enfoque puede ser útil, pero a medida que las expresiones regulares se vuelven más complejas, este enfoque será inmanejable. En su lugar, puede usar variables de estado para cada línea, así:

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

Esto establece que se maneje a cero para cada nueva línea, luego a 1 si coincide con cualquiera de las dos expresiones regulares, y finalmente, si aún es cero, ejecuta la impresión $ 0.

Alex Dupuy
fuente
Cabe señalar que en archivos grandes ambos son menos eficientes que usar condicionales (como se muestra aquí ). rfileson solo 10000 líneas del conjunto de datos del interlocutor repetido.
Chris Down
44
if (!handled)¡Qué asco! Use nextpara dejar de considerar otras acciones.
dmckee
+1 para if (!handled). Las soluciones generales, flexibles y reutilizables son buenas. ¿Qué pasa si la siguiente persona que tiene esta pregunta quiere hacer más procesamiento después de la impresión? Las respuestas con nextno apoyan eso.
Scott