🧙♂️🦄 AWK: una herramienta muy potente para manipular texto en Linux
Utilizando Linux en el mundo profesional, y también en el personal, más de una vez nos vamos a encontrar en la situación de tener que manipular texto y transformar datos. AWK es una gran herramienta para ello.
AWK es una herramienta de línea de comandos en Linux y también es un lenguaje de programación diseñado para procesar textos y datos. No es un simple comando, es prácticamente un lenguaje completo con variables, funciones, condicionales y bucles. Todo metido en un solo comando de terminal.
El nombre AWK viene de las iniciales de sus creadores: Aho, Weinberger y Kernighan, todos estadounidenses. Fue creado en 1977 en los laboratorios Bell de AT&T (el mismo sitio donde nacieron Unix y C), así que lleva casi 50 años haciendo su trabajo y sigue siendo una de las herramientas más usadas en cualquier servidor Linux.
¿Cómo funciona?
AWK trabaja línea por línea. Lee un fichero (o la entrada estándar), divide cada línea en campos y te permite trabajar con ellos. Por defecto, separa los campos por espacios o tabulaciones.
Piensa en ello como una hoja de cálculo en la terminal: cada línea es una fila y cada palabra separada por espacios es una columna. Tú le dices “quiero la columna 2 de las filas que cumplan X condición” y AWK te lo saca al instante.
La línea completa
Primera columna
Segunda columna
Última columna
Sintaxis básica
La estructura general para usar AWK es:
awk 'condición { acción }' fichero.txt
- Condición: Filtra líneas. Por ejemplo,
NR == 1para la primera línea,$2 > 30para líneas donde la segunda columna sea mayor que 30. - Acción: Qué hacer con las líneas que cumplen la condición. Por ejemplo,
print $1para imprimir la primera columna.
Si no pones condición, la acción se aplica a todas las líneas. Si no pones acción, por defecto imprime la línea completa.
Variables especiales de AWK
AWK tiene unas cuantas variables internas que conviene conocer:
Número de línea actual (Number of Records). Se incrementa con cada línea leída.
Número de campos de la línea actual (Number of Fields). $NF es la última columna.
Field Separator. El delimitador de campos. Por defecto es el espacio.
Output Field Separator. El separador para la salida. Por defecto es el espacio.
Record Separator. El separador de registros (líneas). Por defecto es el salto de línea.
El nombre del fichero que se está procesando.
Ejemplos prácticos
Imagina que tenemos el siguiente fichero ejemplo_awk.txt:
PACO 25 Panadero
PEPE 30 Fontanero
JUAN 35 Mecánico
CARLOS 67 Jubilado
Extraer columnas
Queremos obtener únicamente los nombres de las personas del fichero:
awk '{print $1}' ejemplo_awk.txt
🧙 Salida: bash PACO PEPE JUAN CARLOS
¿Queremos extraer la profesión? Solo tendríamos que cambiar la columna a $3:
awk '{print $3}' ejemplo_awk.txt
🧙 Salida: text Panadero Fontanero Mecánico Jubilado
¿Y si queremos nombre y profesión juntos?
awk '{print $1, $3}' ejemplo_awk.txt
🧙 Salida: bash PACO Panadero PEPE Fontanero JUAN Mecánico CARLOS Jubilado
🧙♂️ ¿Fantástico verdad?
Filtrar con condiciones
Queremos extraer únicamente los que estén laboralmente activos, es decir, filtrar por edad y que aparezcan los nombres y las profesiones pero no la edad:
awk '$2 < 65 {print $1, $3}' ejemplo_awk.txt
🧙 Salida: bash PACO Panadero PEPE Fontanero JUAN Mecánico
Más ejemplos de condiciones:
# Solo líneas donde la profesión sea "Fontanero"
awk '$3 == "Fontanero"' ejemplo_awk.txt
# Líneas donde la edad esté entre 25 y 40
awk '$2 >= 25 && $2 <= 40' ejemplo_awk.txt
# Líneas que contengan la letra "A" en el nombre
awk '$1 ~ /A/' ejemplo_awk.txt
Formatear texto
Los nombres están en mayúsculas y no mola mucho, así que vamos a formatearlos. AWK tiene funciones de texto integradas:
awk '{print toupper(substr($1,1,1)) tolower(substr($1,2))}' ejemplo_awk.txt
🧙 Salida: bash Paco Pepe Juan Carlos
Otras funciones de texto útiles:
# Convertir todo a minúsculas
awk '{print tolower($0)}' ejemplo_awk.txt
# Convertir todo a mayúsculas
awk '{print toupper($0)}' ejemplo_awk.txt
# Longitud del nombre
awk '{print $1, length($1)}' ejemplo_awk.txt
# Reemplazar texto
awk '{gsub("Jubilado", "Retirado"); print}' ejemplo_awk.txt
Hacer cálculos
También podemos sacar cálculos de estos datos, como por ejemplo la media de edad de las personas del fichero:
awk '{sum += $2} END {print "Media de edad:", sum/NR}' ejemplo_awk.txt
🧙 Salida: bash Media de edad: 39.25
Más cálculos:
# Suma total de edades
awk '{sum += $2} END {print "Suma total:", sum}' ejemplo_awk.txt
# Edad máxima
awk 'BEGIN {max=0} $2 > max {max=$2; nombre=$1} END {print nombre, max}' ejemplo_awk.txt
# Contar cuántos registros cumplen una condición
awk '$2 < 65 {count++} END {print "Activos:", count}' ejemplo_awk.txt
Los bloques BEGIN y END
AWK tiene dos bloques especiales que son muy útiles:
Se ejecuta antes de procesar la primera línea. Ideal para inicializar variables, imprimir cabeceras o configurar separadores.
Se ejecuta después de procesar la última línea. Perfecto para imprimir totales, medias o resúmenes.
awk '
BEGIN {print "=== INFORME DE PERSONAL ==="; print ""}
$2 < 65 {print $1, "-", $3; count++}
END {print ""; print "Total activos:", count}
' ejemplo_awk.txt
🧙 Salida: ```bash === INFORME DE PERSONAL ===
PACO - Panadero PEPE - Fontanero JUAN - Mecánico
Total activos: 3
---
## Cambiar el separador de campos
Por defecto AWK separa por espacios, pero puedes cambiarlo con `-F`. Esto es brutal para trabajar con CSVs, ficheros de configuración y logs:
```bash
# Procesar un CSV (separado por comas)
awk -F',' '{print $1, $3}' datos.csv
# Procesar /etc/passwd (separado por :)
awk -F':' '{print $1, $7}' /etc/passwd
# Separador de múltiples caracteres
awk -F'::' '{print $1}' fichero.txt
# Cambiar también el separador de salida
awk -F',' 'BEGIN{OFS=" | "} {print $1, $2, $3}' datos.csv
El ejemplo de /etc/passwd es muy típico. Cada línea tiene el formato usuario:x:uid:gid:info:home:shell, separado por :. Con AWK puedes extraer cualquier campo al instante:
# Listar todos los usuarios y su shell
awk -F':' '{print $1, "->", $7}' /etc/passwd
# Solo usuarios con shell bash
awk -F':' '$7 == "/bin/bash" {print $1}' /etc/passwd
# Usuarios con UID mayor que 1000 (usuarios normales)
awk -F':' '$3 > 1000 {print $1, $3}' /etc/passwd
AWK con pipes
AWK se vuelve todavía más potente cuando lo combinas con otros comandos usando pipes (|):
# Espacio en disco por partición (solo las que superan el 50%)
df -h | awk '$5+0 > 50 {print $6, $5}'
# Procesos que consumen más de 1% de CPU
ps aux | awk '$3 > 1.0 {print $11, $3"%"}'
# IPs con más conexiones al servidor
netstat -an | awk '/ESTABLISHED/ {print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head -10
# Top 5 comandos más usados en tu historial
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -5
# Tamaño total de archivos .log
find /var/log -name "*.log" -exec ls -l {} \; | awk '{sum += $5} END {print "Total:", sum/1024/1024, "MB"}'
Procesando logs: donde AWK realmente brilla
Si hay un sitio donde AWK se gana el pan, es procesando ficheros de log. Imagina un log de accesos de Apache/Nginx con el formato típico:
192.168.1.10 - - [15/Mar/2025:10:15:30] "GET /index.html HTTP/1.1" 200 1234
192.168.1.20 - - [15/Mar/2025:10:15:31] "POST /api/login HTTP/1.1" 401 567
192.168.1.10 - - [15/Mar/2025:10:15:32] "GET /css/style.css HTTP/1.1" 200 890
192.168.1.30 - - [15/Mar/2025:10:15:33] "GET /admin HTTP/1.1" 403 234
10.0.0.1 - - [15/Mar/2025:10:15:34] "GET /index.html HTTP/1.1" 200 1234
Con AWK puedes sacar información muy útil de estos logs:
# Contar peticiones por IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn
# Solo errores (códigos 4xx y 5xx)
awk '$9 >= 400 {print $1, $9, $7}' access.log
# Peticiones al endpoint /api/login que fallaron
awk '$7 ~ /\/api\/login/ && $9 != 200 {print $1, $9}' access.log
# Total de bytes transferidos
awk '{sum += $10} END {print "Total:", sum/1024/1024, "MB"}' access.log
# Peticiones por hora
awk '{split($4, a, ":"); print a[2]":00"}' access.log | sort | uniq -c
# IPs que han recibido un 403 (posibles intentos de acceso no autorizado)
awk '$9 == 403 {print $1, $7}' access.log
Condicionales y bucles
AWK es un lenguaje completo, así que tiene if/else, for, while… la herramienta de la terminal convertida en un mini lenguaje de programación:
# Clasificar personas por edad
awk '{
if ($2 < 30) categoria = "Joven"
else if ($2 < 65) categoria = "Adulto"
else categoria = "Jubilado"
print $1, "-", categoria
}' ejemplo_awk.txt
# Imprimir cada campo de una línea en líneas separadas
awk '{for (i=1; i<=NF; i++) print "Campo " i ": " $i}' ejemplo_awk.txt
🧙 Salida del primero: bash PACO - Joven PEPE - Adulto JUAN - Adulto CARLOS - Jubilado
Arrays asociativos
Una de las funcionalidades más potentes de AWK son los arrays asociativos (como los diccionarios de Python o los maps de otros lenguajes):
# Contar ocurrencias de cada profesión
awk '{profesiones[$3]++} END {for (p in profesiones) print p, profesiones[p]}' ejemplo_awk.txt
# Suma de edades por profesión (útil con datos más grandes)
awk '{suma[$3] += $2; count[$3]++} END {
for (p in suma) print p, "- Media:", suma[p]/count[p]
}' ejemplo_awk.txt
Esto es especialmente útil con logs grandes. Imagina contar cuántas peticiones ha hecho cada IP en un log de millones de líneas: AWK lo hace en segundos.
Funciones integradas más útiles
| Función | Descripción | Ejemplo |
|---|---|---|
length(s) | Longitud de un string | length($1) |
substr(s,i,n) | Subcadena desde posición i, n caracteres | substr($1,1,3) |
toupper(s) | Convertir a mayúsculas | toupper($1) |
tolower(s) | Convertir a minúsculas | tolower($1) |
gsub(r,s,t) | Reemplazar todas las ocurrencias de r por s en t | gsub("a","e",$1) |
sub(r,s,t) | Reemplazar solo la primera ocurrencia | sub("a","e",$1) |
split(s,a,d) | Dividir string s en array a usando delimitador d | split($4,arr,":") |
sprintf(f,...) | Formatear string (como printf de C) | sprintf("%05d",$2) |
index(s,t) | Posición de t dentro de s (0 si no está) | index($0,"error") |
Formatear la salida con printf
Si quieres un control más preciso sobre el formato de salida, usa printf en lugar de print:
# Salida formateada con columnas alineadas
awk '{printf "%-10s %3d %s\n", $1, $2, $3}' ejemplo_awk.txt
🧙 Salida: bash PACO 25 Panadero PEPE 30 Fontanero JUAN 35 Mecánico CARLOS 67 Jubilado
Los formatos más comunes de printf:
%s— string%d— entero%f— decimal%-10s— string alineado a la izquierda, mínimo 10 caracteres%05d— entero con ceros a la izquierda, mínimo 5 dígitos
One-liners útiles para el día a día
Estos son comandos de una línea que uso frecuentemente y que te pueden sacar de un apuro:
# Eliminar líneas en blanco de un fichero
awk 'NF > 0' fichero.txt
# Eliminar líneas duplicadas (manteniendo el orden)
awk '!seen[$0]++' fichero.txt
# Imprimir solo líneas entre la 5 y la 10
awk 'NR >= 5 && NR <= 10' fichero.txt
# Imprimir la última columna de cada línea
awk '{print $NF}' fichero.txt
# Invertir el orden de las columnas
awk '{for (i=NF; i>0; i--) printf "%s ", $i; print ""}' fichero.txt
# Sumar una columna numérica de un CSV
awk -F',' '{sum += $3} END {print sum}' datos.csv
# Numerar las líneas de un fichero
awk '{print NR, $0}' fichero.txt
# Imprimir líneas más largas de 80 caracteres
awk 'length > 80' fichero.txt
Conclusión
AWK es una de esas herramientas que cuanto más la usas, más te das cuenta de lo brutalmente potente que es. Con una sola línea puedes hacer cosas que en otros lenguajes te llevarían 20 líneas de código.
Las claves para dominar AWK:
- Empieza con
print $1y ve complicando desde ahí. - Piensa en columnas: AWK ve el mundo como filas y columnas.
- Combínalo con pipes:
cat,grep,sort,uniq… AWK se vuelve imparable con pipes. BEGINyENDson tus amigos para inicializar y resumir.- Para logs es imbatible. Si trabajas con servidores, AWK te va a ahorrar horas.
Y recuerda: man awk siempre está ahí para cuando necesites profundizar.
EA, ¡nos leemos! 🍻
Pon a prueba lo aprendido
1. ¿De dónde viene el nombre AWK?
2. ¿Qué hace $0 en AWK?
3. ¿Qué representa la variable NR en AWK?
4. ¿Cómo cambias el separador de campos para procesar un CSV?
5. ¿Cuándo se ejecuta el bloque BEGIN?
6. ¿Qué hace el comando: awk '{sum += $2} END {print sum/NR}' fichero.txt?
7. ¿Qué diferencia hay entre gsub() y sub() en AWK?
8. ¿Qué hace el comando: awk '!seen[$0]++' fichero.txt?