Modo slurp en awk?


16

Las herramientas tienen gusto sed, awko perl -nprocesan su entrada un registro a la vez, los registros son líneas por defecto.

Algunos, como awkwith RS, GNU sedwith -zor perlwith -0ooopueden cambiar el tipo de registro seleccionando un separador de registro diferente.

perl -npuede hacer que toda la entrada (cada archivo individual cuando se pasan varios archivos) sea un único registro con la -0777opción (o -0seguido de cualquier número octal mayor que 0377, siendo 777 el canónico). Eso es lo que llaman el modo sorber .

¿Se puede hacer algo similar con awk's RSu otro mecanismo? ¿Dónde awkprocesa el contenido de cada archivo en su conjunto en orden en lugar de cada línea de cada archivo?

Respuestas:


15

Puede adoptar diferentes enfoques dependiendo de si se awktrata RScomo un solo carácter (como lo hacen las awkimplementaciones tradicionales ) o como una expresión regular (como gawko mawkhacer). Los archivos vacíos también son difíciles de considerar, ya que awktienden a omitirlos.

gawk, mawkU otras awkimplementaciones en las que RSpueden ser una expresión regular.

En esas implementaciones (para mawk, tenga en cuenta que algunos sistemas operativos como Debian envían una versión muy antigua en lugar de la moderna mantenida por @ThomasDickey ), si RScontiene un solo carácter, el separador de registros es ese carácter o awkingresa al modo de párrafo cuando RSestá vacío, o trata RScomo una expresión regular de lo contrario.

La solución es usar una expresión regular que no se pueda igualar. Algunos vienen a la mente como x^o $x( xantes del comienzo o después del final). Sin embargo, algunos (particularmente con gawk) son más caros que otros. Hasta ahora, he encontrado que ^$es el más eficiente. Solo puede coincidir con una entrada vacía, pero entonces no habría nada contra lo que comparar.

Entonces podemos hacer:

awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

Sin embargo, una advertencia es que omite archivos vacíos (al contrario de perl -0777 -n). Eso se puede abordar con GNU awkcolocando el código en una ENDFILEdeclaración. Pero también necesitamos restablecer $0en una declaración BEGINFILE, ya que de lo contrario no se restablecería después de procesar un archivo vacío:

gawk -v RS='^$' '
   BEGINFILE{$0 = ""}
   ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...

awkimplementaciones tradicionales , POSIXawk

En esos, RSsolo hay un personaje, no tienen BEGINFILE/ ENDFILE, no tienen la RTvariable, generalmente tampoco pueden procesar el carácter NUL.

Pensaría que usar RS='\0'podría funcionar entonces, ya que de todos modos no pueden procesar la entrada que contiene el byte NUL, pero no, RS='\0'en las implementaciones tradicionales se trata como RS=, que es el modo de párrafo.

Una solución puede ser usar un carácter que es poco probable que se encuentre en la entrada como \1. En las configuraciones regionales de caracteres multibyte, incluso puede crear secuencias de bytes que es muy poco probable que ocurran ya que forman caracteres que no están asignados o que no son caracteres como $'\U10FFFE'en las configuraciones regionales UTF-8. Sin embargo, no es realmente infalible y también tiene un problema con los archivos vacíos.

Otra solución puede ser almacenar toda la entrada en una variable y procesarla en la instrucción END al final. Sin embargo, eso significa que solo puede procesar un archivo a la vez:

awk '{content = content $0 RS}
     END{$0 = content
       printf "%s: <%s>\n", FILENAME, $0
     }' file

Eso es el equivalente de sed's:

sed '
  :1
  $!{
   N;b1
  }
  ...' file1

Otro problema con ese enfoque es que si el archivo no terminaba en un carácter de nueva línea (y no estaba vacío), uno todavía se agrega arbitrariamente $0al final (con gawk, evitaría eso al usarlo en RTlugar de RSen el código de arriba). Una ventaja es que tiene un registro del número de líneas en el archivo en NR/ FNR.


en cuanto a la última parte ("si el archivo no terminaba en un carácter de nueva línea (y no estaba vacío), uno todavía se agrega arbitrariamente en $ 0 al final"): para los archivos de texto, se supone que tienen un final nueva línea. vi agrega uno, por ejemplo, y así modifica el archivo cuando lo guarda. Al no tener una nueva línea de terminación, algunos comandos descartan la última "línea" (ej .: wc) pero otros todavía "ven" la última línea ... ymmv. Por lo tanto, su solución es válida, en mi opinión, si se supone que debe tratar los archivos de texto (que probablemente sea el caso, ya que awk es bueno para el procesamiento de texto pero no tan bueno para los archivos binarios ^^)
Olivier Dulac

1
tratar de sorber todo puede tener algunas limitaciones ... el awk tradicional aparentemente tenía (¿tiene?) un límite de 99 campos en una línea ... por lo que es posible que también necesite usar un FS diferente para evitar ese límite, pero puede ¿también tiene límites sobre cuánto puede ser la longitud total de una línea (o todo, si logra obtenerlo todo en una línea)?
Olivier Dulac

finalmente: un truco (tonto ...) podría ser analizar primero el archivo completo y buscar un carácter que no esté allí, luego tr '\n' 'thatchar' el archivo antes de enviarlo a awk y tr 'thatchar' \n'la salida. (es posible que deba agregar una nueva línea para asegurarse, como señalé anteriormente, que su archivo de entrada tiene una nueva línea final: { tr '\n' 'missingchar' < thefile ; printf "\n" ;} | awk ..... | { tr 'missingchar' '\n' }(pero eso agrega un '\ n' al final, de lo que tal vez deba deshacerse de ... tal vez agregar un sed antes del último tr? si ese tr acepta archivos sin terminar las nuevas líneas ...)
Olivier Dulac

@OlivierDulac, el límite en el número de campos solo se alcanzaría si estuviéramos accediendo a NF o cualquier campo. awkno hace la división si no lo hacemos. Dicho esto, ni siquiera el /bin/awkSolaris 9 (basado en el awk de 1970) tenía esa limitación, por lo que no estoy seguro de que podamos encontrar uno que sí lo tenga (aún posible, ya que el roble de SVR4 tenía un límite de 99 y nawk 199, por lo que es probablemente el aumento de ese límite fue agregado por Sun y no se puede encontrar en otros awks basados ​​en SVR4, ¿puede probar en AIX?).
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.