Primeros pasos con el comando AWK [Guía para principiantes]

El comando AWK se remonta a los primeros días de Unix. Es parte del estándar POSIX y debería estar disponible en cualquier sistema tipo Unix. Y más allá.

Aunque a veces se le desacredita por su antigüedad o falta de funciones en comparación con un lenguaje multipropósito como Perl, AWK sigue siendo una herramienta que me gusta usar en mi trabajo diario. A veces para escribir programas relativamente complejos, pero también debido a las poderosas frases ingeniosas que puede escribir para resolver problemas con sus archivos de datos.

Entonces, este es exactamente el propósito de este artículo. Mostrándole cómo puede aprovechar el poder de AWK en menos de 80 caracteres para realizar tareas útiles. Este artículo no pretende ser un tutorial completo de AWK, pero aún así he incluido algunos comandos básicos al principio, por lo que incluso si tienes poca o ninguna experiencia previa, puedes captar los conceptos básicos de AWK.

Mis archivos de muestra para este tutorial de AWK

Todas las frases ingeniosas descritas en ese artículo se probarán en el mismo archivo de datos:

cat file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Puede obtener una copia de ese archivo en línea en GitHub.

Conozca las variables predefinidas y automáticas en AWK

AWK admite un par de variables automáticas y predefinidas para ayudarle a escribir sus programas. Entre ellos encontrarás a menudo:

RS–El separador de registros. AWK procesa sus datos un registro a la vez. El separador de registros es el delimitador utilizado para dividir el flujo de datos de entrada en registros. De forma predeterminada, este es el carácter de nueva línea. Entonces, si no lo cambia, un registro es una línea del archivo de entrada.

NR– El número de registro de entrada actual. Si está utilizando el delimitador de nueva línea estándar para sus registros, esto coincidirá con el número de línea de entrada actual.

FS/OFS–Los caracteres utilizados como separador de campo. Una vez que AWK lee un registro, lo divide en diferentes campos según el valor deFS. Cuando AWK imprima un registro en la salida, volverá a unir los campos, pero esta vez, usando elOFSseparador en lugar delFSseparador. Generalmente,FSyOFSson iguales, pero esto no es obligatorio. “espacio en blanco” es el valor predeterminado para ambos.

NF– El número de campos del registro actual. Si está utilizando el delimitador estándar de "espacio en blanco" para sus campos, esto coincidirá con la cantidad de palabras en el registro actual.

Hay otras variables AWK más o menos estándar disponibles, por lo que vale la pena consultar su manual de implementación de AWK particular para obtener más detalles. Sin embargo, este subconjunto ya es suficiente para empezar a escribir chistes interesantes.

A. Uso básico del comando AWK

1. Imprime todas las líneas

Este ejemplo es prácticamente inútil, pero será una buena introducción a la sintaxis de AWK:

awk '1 { print }' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Los programas AWK están hechos de uno o variospattern { action }declaraciones.

Si, para un registro determinado (“línea”) del archivo de entrada, el patrón se evalúa como un valor distinto de cero (equivalente a “verdadero” en AWK), se ejecutan los comandos en el bloque de acción correspondiente. En el ejemplo anterior, dado que1es una constante distinta de cero, la{ print }El bloque de acción se ejecuta para cada registro de entrada.

Otro truco es{ print }es el bloque de acción predeterminado que utilizará AWK si no especifica uno explícitamente. Entonces el comando anterior se puede acortar como:

awk 1 file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Casi igual de inútil, el siguiente programa AWK consumirá su entrada pero no producirá nada en la salida:

awk 0 file

2. Eliminar el encabezado de un archivo

awk 'NR>1' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Recuerde, esto equivale a escribir explícitamente:

awk 'NR>1 { print }' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

Esta frase escrita escribirá registros del archivo de entrada excepto el primero, ya que en ese caso la condición es1>1lo cual obviamente no es cierto.

Dado que este programa utiliza los valores predeterminados paraRS, en la práctica descartará la primera línea del archivo de entrada.

3. Imprimir líneas en un rango

Esto es sólo una generalización del ejemplo anterior, y no merece muchas explicaciones, excepto decir&&es lo lógicoandoperador:

awk 'NR>1 && NR < 4' file
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team

4. Eliminar líneas que solo contienen espacios en blanco

awk 'NF' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support
17,05 apr 2019,abhishek,guest

AWK divide cada registro en campos, según el separador de campos especificado en elFSvariable. El separador de campo predeterminado es uno o varios caracteres de espacio en blanco (también conocidos como espacios o tabulaciones). Con esa configuración, cualquier registro que contenga al menos un carácter que no sea un espacio en blanco contendrá al menos un campo.

En otras palabras, el único caso en el queNFes 0 (“falso”) es cuando el registro contiene solo espacios. Entonces, esa línea única solo imprimirá registros que contengan al menos un carácter que no sea un espacio.

5. Eliminar todas las líneas en blanco

awk '1' RS='' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support

17,05 apr 2019,abhishek,guest

Esta frase ingeniosa se basa en una oscura regla POSIX que especifica si elRSse establece en la cadena vacía, "entonces los registros se separan por secuencias que consisten en una más una o más líneas en blanco”.

Vale la pena mencionar que en la terminología POSIX, una línea en blanco es una línea completamente vacía. Las líneas que contienen sólo espacios en blanco no cuentan como "en blanco".

6. Extrayendo campos

Este es probablemente uno de los casos de uso más comunes de AWK: extraer algunas columnas del archivo de datos.

awk '{ print $1, $3}' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
,
,
17,abhishek

Aquí, configuro explícitamente los separadores de campo de entrada y salida en coma. Cuando AWK divide un registro en campos, almacena el contenido del primer campo en $1, el contenido del segundo campo en $2 y así sucesivamente. No uso eso aquí, pero vale la pena mencionar que $0 es el registro completo.

En esta frase, habrás notado que uso un bloque de acción sin patrón. En ese caso, se supone 1 (“verdadero”) para el patrón, por lo que el bloque de acción se ejecuta para cada registro.

Dependiendo de sus necesidades, es posible que no produzca lo que nos gustaría para líneas en blanco o solo espacios en blanco. En ese caso, esa segunda versión podría ser un poco mejor:

awk 'NF { print $1, $3 }' FS=, OFS=, file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

En ambos casos, pasé valores personalizados paraFSyOFSen la línea de comando. Otra opción sería utilizar un especialBEGINbloque dentro del programa AWK para inicializar esas variables antes de que se lea el primer registro. Entonces, dependiendo de tus gustos, es posible que prefieras escribir eso:

awk 'BEGIN { FS=OFS="," } NF { print $1, $3 }' file
CREDITS,USER
99,sylvain
52,sonia
52,sonia
25,sonia
10,sylvain
8,öle
        ,
17,abhishek

Vale la pena mencionar aquí que también puedes usarENDbloques para realizar algunas tareas después de que se haya leído el último registro. Como lo veremos ahora mismo. Dicho esto, admito que esto está lejos de ser perfecto ya que las líneas de solo espacios en blanco no se manejan con elegancia. Pronto veremos una posible solución, pero antes hagamos algunos cálculos...

7. Realizar cálculos por columnas

AWK admite operadores aritméticos estándar. Y convertirá valores entre texto y números automáticamente según el contexto. Además, puede utilizar sus propias variables para almacenar valores intermedios. Todo eso le permite escribir programas compactos para realizar cálculos en columnas de datos:

awk '{ SUM=SUM+$1 } END { print SUM }' FS=, OFS=, file
263

O, de manera equivalente, usar el+=sintaxis abreviada:

awk '{ SUM+=$1 } END { print SUM }' FS=, OFS=, file
263

Tenga en cuenta que no es necesario declarar las variables AWK antes de su uso. Se supone que una variable indefinida contiene la cadena vacía. Lo cual, según las reglas de conversión de tipos AWK, es igual al número 0. Debido a esa característica, no me molesté en manejar explícitamente el caso en el que$1contiene texto (en el encabezado), espacios en blanco o simplemente nada. En todos esos casos, contará como 0 y no interferirá con nuestra suma. Por supuesto, sería diferente si en su lugar realizara multiplicaciones. Entonces, ¿por qué no utilizarías la sección de comentarios para sugerir una solución para ese caso?

8. Contando el número de líneas no vacías

Ya he mencionado elENDgobernar antes. Aquí hay otra posible aplicación para contar el número de líneas no vacías en un archivo:

awk '/./ { COUNT+=1 } END { print COUNT }' file
9

Aquí usé elCOUNTvariable y la incrementó (+=1) para cada línea que coincida con la expresión regular/./. Es decir, cada línea contiene al menos un carácter. Finalmente, el bloque END se utiliza para mostrar el resultado final una vez que se ha procesado todo el archivo. No hay nada especial en el nombre.COUNT. podría haber usadoCount,count,n,xxxxo cualquier otro nombre que cumpla con las reglas de nomenclatura de variables de AWK

Sin embargo, ¿es correcto este resultado? Bueno, depende de tu definición de línea "vacía". Si considera que solo las líneas en blanco (según POSIX) están vacías, entonces esto es correcto. ¿Pero tal vez preferirías considerar vacías también las líneas de solo espacios en blanco?

awk 'NF { COUNT+=1 } END { print COUNT }' file
8

Esta vez el resultado es diferente ya que la versión posterior también ignora las líneas de solo espacios en blanco, mientras que la versión inicial solo ignoraba las líneas en blanco. ¿Puedes ver la diferencia? Te dejo que lo averigües tú mismo. ¡No dudes en utilizar la sección de comentarios si esto no está lo suficientemente claro!

Finalmente, si solo está interesado en las líneas de datos y dado mi archivo de datos de entrada particular, podría escribirlo en su lugar:

awk '+$1 { COUNT+=1 } END { print COUNT }' file
7

Funciona gracias a las reglas de conversión de tipos AWK. El signo unario más en el patrón obliga a evaluar 1 dólar en un contexto numérico. En mi expediente, los registros de datos contienen un número en su primer campo. Los registros que no son datos (encabezados, líneas en blanco, líneas solo con espacios en blanco) contienen texto o nada. Todos ellos son iguales a 0 cuando se convierten a números.

Tenga en cuenta que con la última solución, también se descartará un registro de un usuario que eventualmente tenga 0 créditos.

B. Usando matrices en AWK

Las matrices son una característica poderosa de AWK. Todos los arreglos en AWK son arreglos asociativos, por lo que permiten asociar una cadena arbitraria con otro valor. Si está familiarizado con otros lenguajes de programación, es posible que los conozca como hashes, tablas asociativas, diccionarios o mapas.

9. Un ejemplo sencillo de matriz AWK

Imaginemos que quiero saber el crédito total de todos los usuarios. Puedo almacenar una entrada para cada usuario en una matriz asociativa y cada vez que encuentro un registro para ese usuario, incremento el valor correspondiente almacenado en la matriz.

awk '+$1 { CREDITS[$3]+=$1 }
     END { for (NAME in CREDITS) print NAME, CREDITS[NAME] }' FS=, file
abhishek 17
sonia 129
öle 8
sylvain 109

Admito que esto ya no es una frase sencilla. Principalmente debido a laforbucle utilizado para mostrar el contenido de la matriz después de que se haya procesado el archivo. Entonces, volvamos ahora a ejemplos más breves:

10. Identificar líneas duplicadas usando AWK

Los arrays, al igual que otras variables de AWK, se pueden utilizar tanto en bloques de acción como en patrones. Aprovechando eso, podemos escribir una sola línea para imprimir solo líneas duplicadas:

awk 'a[$0]++' file
52,01    dec   2018,sonia,team

El++operador es el operador post-incremento heredado de la familia de lenguajes C (de cuyo AWK es un miembro orgulloso, gracias a que Brian Kernighan ha sido uno de sus autores originales).

Como su nombre lo indica, el operador post-incremento incrementa (“agrega 1”) una variable, pero solo después de que se haya tomado su valor para la evaluación de la expresión envolvente.

En ese caso,a[$0]Se evalúa para ver si el registro se imprimirá o no, y una vez tomada la decisión, en todos los casos, se incrementa la entrada del array.

Entonces, la primera vez que se lee un registro,a[$0]no está definido y, por lo tanto, es equivalente a cero para AWK. Entonces ese primer registro no se escribe en la salida. Luego esa entrada se cambia de cero a uno.
La segunda vez que se lee el mismo registro de entrada,a[$0]ahora es 1. Eso es “verdadero”. La línea será impresa. Sin embargo, antes de eso, la entrada de la matriz se actualiza de 1 a 2. Y así sucesivamente.

11. Eliminar líneas duplicadas

Como corolario de la frase anterior, es posible que deseemos eliminar líneas duplicadas:

awk '!a[$0]++' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support


17,05 apr 2019,abhishek,guest

La única diferencia es el uso del operador lógico, no (!) que invierten el valor de verdad de la expresión. Lo que era falso se vuelve verdad y lo que era verdadero se vuelve falso. Lo lógico no tiene absolutamente ninguna influencia sobre el++incremento posterior que funciona exactamente como antes.

C. Magia separadora de campos y registros

12. Cambiar los separadores de campo

awk '$1=$1' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Ese programa establece elFSyOFSvariable para utilizar una coma como separador de campo de entrada y un punto y coma como separador de campo de salida. Dado que AWK no cambia el registro de salida siempre que no haya cambiado un campo, el$1=$1El truco se utiliza para obligar a AWK a romper el registro y volver a ensamblarlo usando el separador de campo de salida.

Recuerde aquí el bloque de acción predeterminado es{ print }. Entonces podrías reescribirlo de manera más explícita como:

awk '$1=$1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support

17;05 apr 2019;abhishek;guest

Es posible que hayas notado que ambos ejemplos también eliminan líneas vacías. ¿Por qué? Bueno, recuerda las reglas de conversión de AWK: una cadena vacía es "falso". Todas las demás cadenas son "verdaderas". La expresion$1=$1es una afectación que altera$1. Sin embargo, esta también es una expresión. Y se evalúa al valor de$1–que es “falso” para la cadena vacía. Si realmente quieres todas las líneas, es posible que tengas que escribir algo como esto:

awk '($1=$1) || 1 { print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

¿Recuerdas el&&¿operador? Era el Y lógico.||es el OR lógico. El paréntesis es necesario aquí debido a las reglas de precedencia de los operadores. Sin ellos, el patrón se habría interpretado erróneamente como$1=($1 || 1)en cambio. Te dejo como ejercicio que pruebes cómo el resultado hubiera sido diferente en aquel entonces.

Finalmente, si no eres muy aficionado a la aritmética, apuesto a que preferirás esa solución más simple:

awk '{ $1=$1; print }' FS=, OFS=';' file
CREDITS;EXPDATE;USER;GROUPS
99;01 jun 2018;sylvain;team:::admin
52;01    dec   2018;sonia;team
52;01    dec   2018;sonia;team
25;01    jan   2019;sonia;team
10;01 jan 2019;sylvain;team:::admin
8;12    jun   2018;öle;team:support



17;05 apr 2019;abhishek;guest

13. Eliminar múltiples espacios

awk '$1=$1' file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01 dec 2018,sonia,team
52,01 dec 2018,sonia,team
25,01 jan 2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12 jun 2018,öle,team:support
17,05 apr 2019,abhishek,guest

Este es casi el mismo programa que el anterior. Sin embargo, dejé los separadores de campos con sus valores predeterminados. Por lo tanto, se utilizan varios espacios en blanco como separador de campo de entrada, pero solo se utiliza un espacio como separador de campo de salida. Esto tiene el agradable efecto secundario de fusionar múltiples espacios en blanco en un solo espacio.

14. Unir líneas usando AWK

ya hemos usadoOFS, el separador de campo de salida. Como habrás adivinado, tiene laORScontraparte para especificar el separador de registros de salida:

awk '{ print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle    abhishek

Aquí utilicé un espacio después de cada registro en lugar de un carácter de nueva línea. Esta frase breve es suficiente en algunos casos de uso, pero todavía tiene algunos inconvenientes.

Lo más obvio es que no descarta las líneas que solo contienen espacios en blanco (los espacios adicionales después de öle provienen de eso). Entonces, puedo terminar usando una expresión regular simple:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS=' ' file; echo
USER sylvain sonia sonia sonia sylvain öle abhishek

Es mejor ahora, pero todavía existe un posible problema. Será más obvio si cambiamos el separador a algo visible:

awk '/[^[:space:]]/ { print $3 }' FS=, ORS='+' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek+

Hay un separador adicional al final de la línea, porque el separador de campo se escribe después de cada registro. Incluyendo el último.

Para solucionarlo, reescribiré el programa para mostrar un separador personalizado antes del registro, comenzando desde el segundo registro de salida.

awk '/[^[:space:]]/ { print SEP $3; SEP="+" }' FS=, ORS='' file; echo
USER+sylvain+sonia+sonia+sonia+sylvain+öle+abhishek

Como yo mismo me encargo de agregar el separador, también configuro el separador de registros de salida AWK estándar en la cadena vacía. Sin embargo, cuando empiece a lidiar con separadores o formato, puede ser la señal de que debería considerar usar la función printf en lugar de laprintdeclaración. Como lo veremos ahora mismo.

D. Formato de campo

Ya he mencionado la relación entre los lenguajes de programación AWK y C. Entre otras cosas, AWK hereda de la biblioteca estándar del lenguaje C el potenteprintffunción, lo que permite un gran control sobre el formato del texto enviado a la salida.

ElprintfLa función toma un formato como primer argumento, que contiene texto sin formato que se generará palabra por palabra y comodines utilizados para formatear diferentes secciones de la salida. Los comodines se identifican con el%personaje. El ser más común%s(para formatear cadenas),%d(para formato de números enteros) y%f(para formato de números de punto flotante). Como esto puede resultar bastante abstracto, veamos un ejemplo:

awk '+$1 { printf("%s ",  $3) }' FS=, file; echo
sylvain sonia sonia sonia sylvain öle abhishek

Puedes notar, como lo contrario de loprintdeclaración, laprintfLa función no utiliza elOFSyORSvalores. Entonces, si desea algún separador, debe mencionarlo explícitamente como lo hice yo agregando un carácter de espacio al final de la cadena de formato. Éste es el precio a pagar por tener el control total de la producción.

Si bien no es en absoluto un especificador de formato, esta es una excelente ocasión para presentar elnnotación que se puede utilizar en cualquier cadena AWK para representar un carácter de nueva línea.

awk '+$1 { printf("%sn",  $3) }' FS=, file
sylvain
sonia
sonia
sonia
sylvain
öle
abhishek

15. Producir resultados tabulares

AWK impone un formato de datos de registro/campo basado en delimitadores. Sin embargo, utilizando elprintffunción, también puede producir una salida tabular de ancho fijo. Debido a que cada especificador de formato en unprintfLa declaración puede aceptar un parámetro de ancho opcional:

awk '+$1 { printf("%10s | %4dn",  $3, $1) }' FS=, file
   sylvain |   99
     sonia |   52
     sonia |   52
     sonia |   25
   sylvain |   10
       öle |    8
  abhishek |   17

Como puede ver, al especificar el ancho de cada campo, AWK los rellena hacia la izquierda con espacios. Para el texto, normalmente es preferible rellenar a la derecha, algo que se puede lograr utilizando un número de ancho negativo. Además, para los números enteros, es posible que deseemos rellenar los campos con ceros en lugar de espacios. Esto se puede obtener usando un 0 explícito antes del ancho del campo:

awk '+$1 { printf("%-10s | %04dn",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. Manejo de números de coma flotante

El%fEl formato no merece muchas explicaciones…

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%f",SUM/NUM); }' FS=, file
AVG=37.571429

… excepto tal vez para decir que casi siempre deseas establecer explícitamente el ancho del campo y la precisión del resultado mostrado:

awk '+$1 { SUM+=$1; NUM+=1 } END { printf("AVG=%6.1f",SUM/NUM); }' FS=, file
AVG=  37.6

Aquí, el ancho del campo es 6, lo que significa que el campo ocupará el espacio de 6 caracteres (incluido el punto y, finalmente, se rellenará con espacios a la izquierda, como suele ocurrir). La precisión de .1 significa que queremos mostrar el número con 1 decimal después del punto. Te dejo adivinar que%06.1se mostraría en su lugar.

E. Usar funciones de cadena en AWK

Además deprintffunción, AWK contiene algunas otras funciones interesantes de manipulación de cadenas. En ese ámbito, las implementaciones modernas como Gawk tienen un conjunto más rico de funciones internas al precio de una menor portabilidad. Por mi parte, me quedaré aquí con solo algunas funciones definidas por POSIX que deberían funcionar igual en cualquier lugar.

17. Convertir texto a mayúsculas

Este lo uso mucho porque maneja muy bien los problemas de internacionalización:

awk '$3 { print toupper($0); }' file
99,01 JUN 2018,SYLVAIN,TEAM:::ADMIN
52,01    DEC   2018,SONIA,TEAM
52,01    DEC   2018,SONIA,TEAM
25,01    JAN   2019,SONIA,TEAM
10,01 JAN 2019,SYLVAIN,TEAM:::ADMIN
8,12    JUN   2018,ÖLE,TEAM:SUPPORT
17,05 APR 2019,ABHISHEK,GUEST

De hecho, esta es probablemente la mejor y más portátil solución para convertir texto a mayúsculas desde el shell.

18. Cambiando parte de una cuerda

Utilizando elsubstrcomando, puede dividir una cadena de caracteres en una longitud determinada. Aquí lo uso para poner en mayúscula sólo el primer carácter del tercer campo:

awk '{ $3 = toupper(substr($3,1,1)) substr($3,2) } $3' FS=, OFS=, file
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,Sylvain,team:::admin
52,01    dec   2018,Sonia,team
52,01    dec   2018,Sonia,team
25,01    jan   2019,Sonia,team
10,01 jan 2019,Sylvain,team:::admin
8,12    jun   2018,Öle,team:support
17,05 apr 2019,Abhishek,guest

ElsubstrLa función toma la cadena inicial, el índice (basado en 1) del primer carácter a extraer y el número de caracteres a extraer. Si falta ese último argumento,substrtoma todos los caracteres restantes de la cadena.

Entonces,substr($3,1,1)evaluará al primer carácter de$3, ysubstr($3,2)a los restantes.

19. Dividir campos en subcampos

El modelo de datos de campo de registro de AWK es realmente bueno. Sin embargo, a veces desea dividir los campos en varias partes según algún separador interno:

awk '+$1 { split($2, DATE, " "); print $1,$3, DATE[2], DATE[3] }' FS=, OFS=, file
99,sylvain,jun,2018
52,sonia,dec,2018
52,sonia,dec,2018
25,sonia,jan,2019
10,sylvain,jan,2019
8,öle,jun,2018
17,abhishek,apr,2019

Sorprendentemente, esto funciona incluso si algunos de mis campos están separados por más de un espacio en blanco. Principalmente por razones históricas, cuando el separador es un espacio único,splitconsiderará "los elementos están separados por espacios en blanco". Y no sólo por uno solo. ElFSLa variable especial sigue la misma convención.

Sin embargo, en el caso general, una cadena de caracteres coincide con un carácter. Entonces, si necesitas algo más complejo, debes recordar que el separador de campo es una expresión regular extendida.

Como ejemplo, veamos cómo se manejaría el campo de grupo que parece ser un campo multivalor usando dos puntos como separador:

awk '+$1 { split($4, GRP, ":"); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team
sonia team
sonia team
sonia team
sylvain team
öle team support
abhishek guest

Mientras que hubiera esperado mostrar hasta dos grupos por usuario, muestra sólo uno para la mayoría de ellos. Ese problema se debe a las múltiples apariciones del separador. Entonces, la solución es:

awk '+$1 { split($4, GRP, /:+/); print $3, GRP[1], GRP[2] }' FS=, file
sylvain team admin
sonia team
sonia team
sonia team
sylvain team admin
öle team support
abhishek guest

Las barras diagonales en lugar de las comillas indican que el literal es una expresión regular en lugar de una cadena simple, y el signo más indica que esta expresión coincidirá con una o varias apariciones del carácter anterior. Entonces, en ese caso, cada separador se compone (de la secuencia más larga de) uno o varios dos puntos consecutivos.

20. Buscar y reemplazar con comandos AWK

Hablando de expresiones regulares, a veces quieres realizar una sustitución como la seds///gcomando, pero sólo en un campo. ElgsubEl comando es lo que necesitas en ese caso:

awk '+$1 { gsub(/ +/, "-", $2); print }' FS=, file
99 01-jun-2018 sylvain team:::admin
52 01-dec-2018 sonia team
52 01-dec-2018 sonia team
25 01-jan-2019 sonia team
10 01-jan-2019 sylvain team:::admin
8 12-jun-2018 öle team:support
17 05-apr-2019 abhishek guest

ElgsubLa función toma una expresión regular para buscar, una cadena de reemplazo y la variable que contiene el texto que se va a modificar en su lugar. Si falta ese valor posterior, se supone $0.

F. Trabajar con comandos externos en AWK

Otra gran característica de AWK es que puedes invocar fácilmente comandos externos para procesar tus datos. Básicamente hay dos formas de hacerlo: utilizando elsysteminstrucción para invocar un programa y permitirle mezclar su salida en el flujo de salida AWK. O usar una tubería para que AWK pueda capturar la salida del programa externo para un control más preciso del resultado.

Estos pueden ser temas extensos por sí solos, pero aquí hay algunos ejemplos simples para mostrarle el poder detrás de esas características.

21. Agregar la fecha encima de un archivo

awk 'BEGIN { printf("UPDATED: "); system("date") } /^UPDATED:/ { next } 1' file
UPDATED: Thu Feb 15 00:31:03 CET 2018
CREDITS,EXPDATE,USER,GROUPS
99,01 jun 2018,sylvain,team:::admin
52,01    dec   2018,sonia,team
52,01    dec   2018,sonia,team
25,01    jan   2019,sonia,team
10,01 jan 2019,sylvain,team:::admin
8,12    jun   2018,öle,team:support



17,05 apr 2019,abhishek,guest

En ese programa AWK, comienzo mostrando el trabajo ACTUALIZADO. Luego, el programa invoca el comando de fecha externo, que enviará su resultado en la salida justo después del texto producido por AWK en esa etapa.
El resto del programa AWK simplemente elimina una declaración de actualización eventualmente presente en el archivo e imprime todas las demás líneas (con la regla1).

Observe lanextdeclaración. Se utiliza para cancelar el procesamiento del registro actual. Es una forma estándar de ignorar algunos registros del archivo de entrada.

22. Modificar un campo externamente

Para casos más complejos, es posible que deba considerar la| getline VARIABLEmodismo de AWK:

awk '+$1 { CMD | getline $5; close(CMD); print }' CMD="uuid -v4" FS=, OFS=, file
99,01 jun 2018,sylvain,team:::admin,5e5a1bb5-8a47-48ee-b373-16dc8975f725
52,01    dec   2018,sonia,team,2b87e9b9-3e75-4888-bdb8-26a9b34facf3
52,01    dec   2018,sonia,team,a5fc22b5-5388-49be-ac7b-78063cbbe652
25,01    jan   2019,sonia,team,3abb0432-65ef-4916-9702-a6095f3fafe4
10,01 jan 2019,sylvain,team:::admin,592e9e80-b86a-4833-9e58-1fe2428aa2a2
8,12    jun   2018,öle,team:support,3290bdef-fd84-4026-a02c-46338afd4243
17,05 apr 2019,abhishek,guest,e213d756-ac7f-4228-818f-1125cba0810f

Esto ejecutará el comando almacenado en elCMDvariable, lea la primera línea de la salida de ese comando y guárdela en la variable$5.

Preste especial atención a la declaración close, crucial aquí ya que queremos que AWK cree una nueva instancia del comando externo cada vez que ejecuta elCMD | getlinedeclaración. Sin la declaración de cierre, AWK intentaría leer varias líneas de salida de la misma instancia de comando.

23. Invocar comandos generados dinámicamente

Los comandos en AWK son simplemente cadenas sin nada especial. Es el operador de tubería el que desencadena la ejecución de programas externos. Entonces, si lo necesita, puede construir dinámicamente comandos complejos arbitrarios utilizando las funciones y operadores de manipulación de cadenas de AWK.

awk '+$1 { cmd = sprintf(FMT, $2); cmd | getline $2; close(cmd); print }' FMT='date -I -d "%s"'  FS=, file
99 2018-06-01 sylvain team:::admin
52 2018-12-01 sonia team
52 2018-12-01 sonia team
25 2019-01-01 sonia team
10 2019-01-01 sylvain team:::admin
8 2018-06-12 öle team:support
17 2019-04-05 abhishek guest

Ya hemos conocido aprintffunción.sprintfes muy similar pero devolverá la cadena construida en lugar de enviarla a la salida.

24. Unir datos

Para mostrarte el propósito de la declaración de cierre, te dejo probar el último ejemplo:

awk '+$1 { CMD | getline $5; print }' CMD='od -vAn -w4 -t x /dev/urandom' FS=, file
99 01 jun 2018 sylvain team:::admin  1e2a4f52
52 01    dec   2018 sonia team  c23d4b65
52 01    dec   2018 sonia team  347489e5
25 01    jan   2019 sonia team  ba985e55
10 01 jan 2019 sylvain team:::admin  81e9a01c
8 12    jun   2018 öle team:support  4535ba30
17 05 apr 2019 abhishek guest  80a60ec8

A diferencia del ejemplo que utiliza eluuidcomando anterior, aquí solo hay una instancia deodse lanza mientras se ejecuta el programa AWK, y al procesar cada registro, leemos una línea más de la salida de ese mismo proceso.

Conclusión

Ese recorrido rápido por AWK ciertamente no puede reemplazar un curso o tutorial completo sobre esa herramienta. Sin embargo, para aquellos de ustedes que no estaban familiarizados con él, espero que les haya dado suficientes ideas para que puedan agregar AWK inmediatamente a su caja de herramientas.

Por otro lado, si ya eras un aficionado a AWK, es posible que hayas encontrado aquí algunos trucos que puedes utilizar para ser más eficiente o simplemente para impresionar a tus amigos.

Sin embargo, no pretendo ser exhaustivo. Entonces, en todos los casos, ¡no dudes en compartir tu frase favorita de AWK o cualquier otro consejo de AWK usando la sección de comentarios a continuación!

Actualizaciones del boletín

Ingrese su dirección de correo electrónico a continuación para suscribirse a nuestro boletín