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á:
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, este coincidirá con el número de línea de entrada actual.
FS/OFS –El(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
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!