GNU/Linux >> Tutoriales Linux >  >> Linux

Primeros pasos con AWK Command [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 similar a Unix. Y más allá.

Si bien a veces está desacreditado debido a 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. Le mostramos 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 tiene poca o ninguna experiencia previa, puede comprender 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.

Conocer variables predefinidas y automaticas en AWK

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

RSEl 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.

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

FS/OFSEl(los) carácter(es) utilizado(s) como separador de campo. Una vez que AWK lee un registro, lo divide en diferentes campos según el valor de FS . Cuando AWK imprima un registro en la salida, volverá a unir los campos, pero esta vez, usando el OFS separador en lugar de FS separador. Por lo general, FS y OFS son iguales, pero esto no es obligatorio. "espacio en blanco" es el valor predeterminado para ambos.

NF – El número de campos en el registro actual. Si está utilizando el delimitador de "espacio en blanco" estándar 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 particular de AWK para obtener más detalles. Sin embargo, este subconjunto ya es suficiente para comenzar a escribir frases interesantes.

A. Uso básico del comando AWK

1. Imprimir todas las líneas

Este ejemplo es en su mayoría inútil, pero de todos modos 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 varios pattern { action } declaraciones.

Si, para un registro dado (“línea”) del archivo de entrada, el patrón evalúa a un valor distinto de cero (equivalente a "verdadero" en AWK), los comandos en el bloque de acción correspondiente son ejecutados. En el ejemplo anterior, desde 1 es una constante distinta de cero, el { 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 un encabezado de 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

Recuerda, esto es el equivalente 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

Este one-liner escribirá registros del archivo de entrada excepto el primero ya que en ese caso la condición es 1>1 lo cual obviamente no es cierto.

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

3. Imprimir líneas en un rango

Esto es solo una generalización del ejemplo anterior, y no merece muchas explicaciones, excepto decir && es el and lógico operador:

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

4. Eliminar líneas de solo 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 campo especificado en el FS variable. El separador de campo predeterminado es uno-o-varios-espacios-en-blanco-caracteres (también conocido 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 donde NF es 0 (“falso”) es cuando el registro contiene solo espacios. Por lo tanto, esa sola línea solo imprimirá registros que contengan al menos un carácter que no sea un espacio.

5. Eliminando 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 se basa en una oscura regla POSIX que especifica si RS se 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 solo espacios en blanco no cuentan como "en blanco".

6. Extrayendo campos

Este es probablemente uno de los casos de uso más comunes para 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 lo uso 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 un patrón. En ese caso, se asume 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 para FS y OFS en la línea de comando. Otra opción sería usar un BEGIN especial bloque dentro del programa AWK para inicializar esas variables antes de que se lea el primer registro. Entonces, dependiendo de tu gusto, quizás prefieras escribir eso en su lugar:

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 puede usar END bloques 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. Realización de cálculos por columnas

AWK admite los operadores aritméticos estándar. Y convertirá valores entre texto y números automáticamente según el contexto. Además, puede usar sus propias variables para almacenar valores intermedios. Todo lo que 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, usando += 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. Que, según las reglas de conversión de tipo AWK, es igual al número 0. Debido a esa característica, no me molesté en manejar explícitamente el caso donde $1 contiene 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 hiciera multiplicaciones en su lugar. 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 el END regla 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é el COUNT variable y la incrementó (+=1 ) para cada línea que coincida con la expresión regular /./ . Esa es cada línea que contiene al menos un carácter. Finalmente, el bloque END se usa 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 usado Count , count , n , xxxx o cualquier otro nombre que cumpla con las reglas de nomenclatura de variables AWK

Sin embargo, ¿es correcto este resultado? Bueno, depende de su definición de una 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, ¿quizás preferiría considerar las líneas de solo espacios en blanco como vacías también?

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 en blanco, mientras que la versión inicial solo ignora las líneas en blanco. ¿Puedes ver la diferencia? Te dejo que lo descubras por ti mismo. ¡No dudes en usar la sección de comentarios si esto no es 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 escribir eso en su lugar:

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

Funciona debido a las reglas de conversión de tipo AWK. El plus unario en el patrón fuerza la evaluación de $1 en un contexto numérico. En mi archivo, los registros de datos contienen un número en su primer campo. Los registros que no son de datos (encabezado, líneas en blanco, líneas de solo espacios en blanco) contienen texto o nada. Todos ellos son iguales a 0 cuando se convierten a números.

Observe que con la última solución, también se descartaría un registro para un usuario que eventualmente tenga 0 créditos.

B. Uso de matrices en AWK

Los arreglos son una característica poderosa de AWK. Todas las matrices en AWK son matrices asociativas, por lo que permiten asociar una cadena arbitraria con otro valor. Si está familiarizado con otros lenguajes de programación, puede conocerlos como hashes , tablas asociativas , diccionarios o mapas .

9. Un ejemplo simple de matriz AWK

Imaginemos que quiero saber el crédito total para 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 sola línea. Principalmente debido a for bucle utilizado para mostrar el contenido de la matriz después de que se haya procesado el archivo. Entonces, volvamos ahora a ejemplos más cortos:

10. Identificando líneas duplicadas usando AWK

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

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

El ++ operator es el operador de incremento posterior heredado de la familia del lenguaje C (cuya AWK es un orgulloso miembro, gracias a que Brian Kernighan fue uno de sus autores originales).

Como su nombre lo indica, el operador de incremento posterior incrementa ("agregar 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 se imprimirá o no el registro, y una vez tomada la decisión, en todos los casos, se incrementa la entrada del arreglo.

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. Entonces 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 se imprimirá. 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 del comentario anterior, es posible que deseemos eliminar las 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 verdadero, y lo que era verdadero se vuelve falso. El no lógico no tiene absolutamente ninguna influencia en el ++ incremento posterior que funciona exactamente como antes.

C. Magia de separación de registros y campos

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 el FS y OFS variable para usar 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=$1 El truco se usa para obligar a AWK a romper el registro y volver a ensamblarlo usando el separador de campo de salida.

Recuerde aquí que el bloque de acción predeterminado es { print } . Entonces podrías reescribir eso más explícitamente 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 haya 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 "falsa". Todas las demás cadenas son "verdaderas". La expresión $1=$1 es una afectación que altera $1 . Sin embargo, esta es una expresión también. Y se evalúa al valor de $1 –que es “falso” para la cadena vacía. Si realmente desea todas las líneas, es posible que deba escribir algo como eso:

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 AND 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 para que pruebes cómo el resultado hubiera sido diferente entonces.

Finalmente, si no te gusta mucho 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 varios 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 campo en 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últiplos espacios en blanco en uno espacio.

14. Uniendo líneas usando AWK

Ya hemos usado OFS , el separador de campo de salida. Como habrás adivinado, tiene el ORS contraparte para especificar el separador de registro de salida:

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

Aquí, usé un espacio después de cada registro en lugar de un carácter de nueva línea. Este one-liner es suficiente en algunos casos de uso, pero aún tiene algunos inconvenientes.

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

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

Es mejor ahora, pero todavía hay 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. Incluido el último.

Para arreglar eso, reescribiré el programa para mostrar un separador personalizado antes el registro, a partir del segundo registro de salida.

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

Dado que me ocupo de agregar el separador yo mismo, también configuro el separador de registro de salida AWK estándar en la cadena vacía. Sin embargo, cuando comience a lidiar con los separadores o el formato, puede ser la señal que debería considerar usar el printf función en lugar de print declaració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, de la biblioteca estándar del lenguaje C, AWK hereda el poderoso printf función, lo que permite un gran control sobre el formato del texto enviado a la salida.

El printf La 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 más común es %s (para formato de cadena), %d (para formato de números enteros) y %f (para formato de número de coma 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

Puede notar, como lo contrario de print declaración, el printf la función no utiliza el OFS y ORS valores. Entonces, si desea algún separador, debe mencionarlo explícitamente como lo hice al agregar un carácter de espacio al final de la cadena de formato. Este es el precio a pagar por tener el control total de la salida.

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

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

15. Producción de resultados tabulares

AWK impone un formato de datos de registro/campo basado en delimitadores. Sin embargo, usando el printf función, también puede producir una salida tabular de ancho fijo. Porque cada especificador de formato en un printf declaración puede aceptar un parámetro de ancho opcional:

awk '+$1 { printf("%10s | %4d\n",  $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 a la izquierda con espacios. Para el texto, normalmente es preferible rellenar a la derecha, algo que se puede lograr usando un número de ancho negativo. Además, para los números enteros, nos gustaría 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 | %04d\n",  $3, $1) }' FS=, file
sylvain    | 0099
sonia      | 0052
sonia      | 0052
sonia      | 0025
sylvain    | 0010
öle        | 0008
abhishek   | 0017

16. Tratar con números de coma flotante

El %f formato no merece muchas explicaciones…

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

… excepto quizás para decir que casi siempre desea 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 eventualmente se rellenará con espacios a la izquierda como de costumbre). La precisión de .1 significa que queremos mostrar el número con 1 número decimal después del punto. Te dejo adivinar qué %06.1 se mostraría en su lugar.

E. Uso de funciones de cadena en AWK

Además del printf función, AWK contiene algunas otras buenas funciones de manipulación de cadenas. En ese dominio, 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 unas pocas funciones definidas por POSIX que deberían funcionar igual en cualquier lugar.

17. Conversión de 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. Cambiar parte de una cadena

Usando el substr comando, puede dividir una cadena de caracteres en una longitud determinada. Aquí lo uso para poner en mayúsculas solo 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

El substr La 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, substr toma todos los caracteres restantes de la cadena.

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

19. División de 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 en función de 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 solo espacio, split considerará "los elementos están separados por espacios en blanco". Y no solo por uno solo. El FS variable especial sigue la misma convención.

Sin embargo, en el caso general, una cadena de caracteres coincide con un carácter. Entonces, si necesita algo más complejo, debe 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 de varios valores utilizando 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 solo uno para la mayoría de ellos. Ese problema es causado por las múltiples ocurrencias 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 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 desea realizar una sustitución como sed s///g comando, pero sólo en un campo. El gsub comando es lo que necesita 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

El gsub La función toma una expresión regular para buscar, una cadena de reemplazo y la variable que contiene el texto que se modificará en su lugar. Si falta eso más tarde, se asume $0.

F. Trabajar con comandos externos en AWK

Otra gran característica de AWK es que puede invocar fácilmente comandos externos para procesar sus datos. Básicamente hay dos formas de hacerlo:usando el system instrucción para invocar un programa y dejar que mezcle su salida en el flujo de salida AWK. O usar una canalización para que AWK pueda capturar la salida del programa externo para un control más preciso del resultado.

Esos pueden ser grandes temas en sí mismos, 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, empiezo mostrando el trabajo ACTUALIZADO. Luego, el programa invoca la date externa comando, 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 regla 1 ).

Observe el next declaración. Se utiliza para abortar el procesamiento del registro actual. It is a standard way of ignoring some records from the input file.

22. Modifying a field externally

For more complex cases, you may need to consider the | getline VARIABLE idiom of 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

This will run the command stored in the CMD variable, read the first line of the output of that command, and store it into the variable $5 .

Pay special attention to the close statement, crucial here as we want AWK to create a new instance of the external command each time it executes the CMD | getline statement. Without the close statement, AWK would instead try to read several lines of output from the same command instance.

23. Invoking dynamically generated commands

Commands in AWK are just plain strings without anything special. It is the pipe operator that triggers external programs execution. So, if you need, you can dynamically construct arbitrary complex commands by using the AWK string manipulation functions and operators.

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

We have already met the printf función. sprintf is very similar but will return the built string rather than sending it to the output.

24. Joining data

To show you the purpose of the close statement, I let you try out that last example:

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

As the opposite of the example using the uuid command above, there is here only one instance of od launched while the AWK program is running, and when processing each record, we read one more line of the output of that same process.

Conclusión

That quick tour of AWK certainly can’t replace a full-fledged course or tutorial on that tool. However, for those of you that weren’t familiar with it, I hope it gave you enough ideas so you can immediately add AWK to your toolbox.

On the other hand, if you were already an AWK aficionado, you might have found here some tricks you can use to be more efficient or simply to impress your friends.

However, I do not pretend been exhaustive. So, in all cases, don’t hesitate to share your favorite AWK one-liner or any other AWK tips using the comment section below!


Linux
  1. Primeros pasos con awk, una poderosa herramienta de análisis de texto

  2. Comenzando con Zsh

  3. Primeros pasos con el comando tac de Linux

  4. Primeros pasos con el comando cat de Linux

  5. Comenzando con ls

Introducción a los ejemplos de comandos de Alpine Linux Apk

Primeros pasos con Nano Text Editor [Guía para principiantes]

Primeros pasos con Markdown [Guía para principiantes]

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

Primeros pasos con Tmux [Guía para principiantes]

Primeros pasos con el comando Tar