Funciones de awk, formato con printf y generación de informes
Defina funciones propias y use printf para generar informes tabulares bien presentados a partir de flujos de datos sin procesar.
Funciones de awk, formato con printf y generación de informes es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
Por qué son importantes las funciones y printf en awk
A medida que sus programas de awk crecen, pasando de comandos de una sola línea a canalizaciones de varios pasos, dos características se vuelven esenciales: las funciones definidas por el usuario y el formato con printf.
Las funciones permiten encapsular lógica reutilizable —un cálculo de porcentajes, un recorte de cadenas o una conversión de unidades— para escribirla una vez y llamarla en cualquier lugar del mismo programa. printf permite controlar exactamente el aspecto de la salida: anchos de columna, posiciones decimales, relleno y alineación.
Juntas, estas características transforman líneas de registro sin procesar en informes pulidos y fáciles de leer sobre los que los ingenieros y responsables pueden actuar.
- Las funciones reducen la duplicación y permiten probar los programas de forma aislada.
printfalinea los datos en columnas de ancho fijo para que los informes sigan siendo legibles incluso con longitudes de entrada variables.- Ambas características funcionan en POSIX awk, gawk y mawk; no se necesitan extensiones.
Definir una función de usuario en awk
Una función de usuario se declara con la palabra clave function, antes o después de cualquier regla de patrón-acción. La sintaxis es:
function name(param1, param2, local1, local2) {
# body
return value
}Un idioma característico de awk: las variables locales son simplemente parámetros adicionales separados de los anteriores por más espacios. No existe ninguna palabra clave local; la convención de usar espacios adicionales indica que esos parámetros son locales y no argumentos que proporcione quien llama a la función.
- Todas las variables que no se declaran como parámetros son globales de forma predeterminada.
- Las funciones pueden llamarse a sí mismas de forma recursiva.
returnes opcional; sin él, la función devuelve una cadena vacía.
#!/usr/bin/env bash
# Demonstrate a simple awk user function
echo '10 3
7 0
100 4' | awk '
function divide(a, b, result) {
if (b == 0) return "ERR"
result = a / b
return result
}
{
print $1 "/" $2 " = " divide($1, $2)
}
'Funciones con lógica de cadenas
Las funciones son especialmente útiles para repetir operaciones con cadenas. Considere la eliminación de espacios en blanco iniciales y finales, algo que los datos de registros sin procesar suelen necesitar antes de compararlos o generar informes.
El ejemplo siguiente define trim(s) mediante gsub y lo utiliza en cada registro, manteniendo la regla principal limpia y legible.
gsub(regex, replacement, target)modifica target directamente y devuelve el número de sustituciones.- Al colocar la lógica de expresiones regulares dentro de una función, el bloque de reglas se mantiene centrado en la lógica de negocio.
- Puede realizar pruebas unitarias pasando cadenas creadas manualmente directamente en un bloque
BEGIN.
#!/usr/bin/env bash
# trim() strips leading/trailing spaces; used to normalise CSV-like data
printf ' alice , 90 \n bob , 85 \n carol , 92 \n' | awk -F',' '
function trim(s) {
gsub(/^[ \t]+|[ \t]+$/, "", s)
return s
}
{
name = trim($1)
score = trim($2)
print name, score
}
'Introducción a printf en awk
printf en awk sigue las mismas convenciones que en C y en el comando integrado printf de Bash. La diferencia principal respecto a print es que printf nunca añade automáticamente un salto de línea; debe añadir \n usted mismo.
Especificadores de formato habituales:
%s— cadena%d— entero%f— coma flotante%e— notación científica%g— el más corto entre%f/%e
El ancho y la precisión se controlan colocando números entre % y el especificador: %-20s alinea a la izquierda en un campo de 20 caracteres; %8.2f proporciona un número de coma flotante de ancho 8 con 2 posiciones decimales.
#!/usr/bin/env bash
# printf format specifiers in awk
echo '' | awk '
BEGIN {
printf "%s\n", "plain string"
printf "%10s\n", "right-pad"
printf "%-10s|\n", "left-pad"
printf "%8.2f\n", 3.14159
printf "%08d\n", 42
printf "%e\n", 123456.789
}
'Crear el encabezado de un informe con printf
Un informe bien elaborado necesita una fila de encabezado y una línea separadora. El bloque BEGIN es el lugar natural para generarlas, ya que se ejecuta una vez antes de procesar cualquier entrada.
El truco consiste en hacer coincidir los anchos del encabezado exactamente con los anchos utilizados en las filas de datos. Definir constantes de ancho en BEGIN (o en una función) mantiene todo sincronizado cuando posteriormente ajuste los anchos de las columnas.
- Use
printfcon una cadena separadora de guiones para dibujar la línea divisoria. - Termine siempre las líneas del encabezado con
\n. - Combine un encabezado en
BEGIN, filas por registro y un pie enENDpara obtener una estructura de informe completa.
#!/usr/bin/env bash
# Print a report header in BEGIN, data rows per-record, total in END
printf 'alice 9200 12\nbob 8750 10\ncarol 10400 14\n' | awk '
BEGIN {
printf "%-10s %10s %6s %12s\n", "Name", "Salary", "Months", "Annual"
printf "%s\n", "----------------------------------------------"
total = 0
}
{
annual = $2 * $3
total += annual
printf "%-10s %10d %6d %12d\n", $1, $2, $3, annual
}
END {
printf "%s\n", "----------------------------------------------"
printf "%-10s %10s %6s %12d\n", "TOTAL", "", "", total
}
'Funciones de usuario para la lógica de formato
Cuando crea informes con varias columnas, es habitual necesitar la misma llamada de formato en varios lugares; por ejemplo, para representar una barra de porcentaje o convertir bytes a una unidad legible. Colocarla dentro de una función evita la duplicación y facilita cambiar el formato de manera global.
La función siguiente convierte bytes a KB, MB o GB y devuelve una cadena con formato. La regla que la llama simplemente pasa $NF (el último campo) e imprime lo que recibe.
#!/usr/bin/env bash
# human_bytes() converts raw byte counts to KB/MB/GB strings
printf 'var.log 1024\naccess.log 2097152\ncore.dump 1073741824\ntiny.txt 512\n' | awk '
function human_bytes(n, s) {
if (n >= 1073741824) { s = sprintf("%.1f GB", n/1073741824) }
else if (n >= 1048576) { s = sprintf("%.1f MB", n/1048576) }
else if (n >= 1024) { s = sprintf("%.1f KB", n/1024) }
else { s = sprintf("%d B", n) }
return s
}
BEGIN { printf "%-20s %10s\n", "File", "Size"; print "------------------------------" }
{ printf "%-20s %10s\n", $1, human_bytes($2) }
'Acumulación de datos en arrays antes de imprimir
Los informes reales suelen necesitar totales, promedios o clasificaciones: valores que no se pueden calcular hasta haber visto toda la entrada. El patrón es el siguiente:
- Acumule los datos en arrays asociativos durante la fase por registro.
- Calcule los valores derivados (promedios, porcentajes) en
END. - Genere el resultado con formato únicamente desde
END, para que los anchos puedan adaptarse al conjunto de datos completo.
Este patrón de salida diferida es uno de los modismos más potentes para generar informes con awk. El ejemplo siguiente contabiliza las solicitudes y los bytes de respuesta de cada código de estado HTTP a partir de un registro de acceso.
#!/usr/bin/env bash
# Summarise an nginx-style access log by HTTP status code
printf '127.0.0.1 200 1234\n127.0.0.1 404 512\n10.0.0.2 200 8900\n10.0.0.3 500 256\n10.0.0.4 200 4096\n10.0.0.5 404 300\n' | awk '
{
status = $2
bytes = $3
count[status]++
total_bytes[status] += bytes
}
END {
printf "%-8s %8s %14s\n", "Status", "Requests", "Total Bytes"
print "-----------------------------------"
for (s in count)
printf "%-8s %8d %14d\n", s, count[s], total_bytes[s]
}
'Funciones recursivas en awk
awk admite la recursividad. Un caso de uso clásico es calcular factoriales o generar cadenas repetitivas (como una línea de guiones con el ancho del terminal). La recursividad es menos habitual en awk que en los lenguajes de propósito general, pero funciona correctamente y está disponible en las principales implementaciones.
El ejemplo siguiente define una función recursiva repeat(s, n) y la utiliza para dibujar líneas separadoras que coinciden automáticamente con el ancho de la fila de datos más ancha, sin necesidad de guiones escritos explícitamente.
- Evite la recursividad profunda con entradas grandes: awk no tiene optimización de llamadas de cola.
- Para repeticiones sencillas,
sprintf("%*s", n, "")junto congsub(/ /, "-")es más rápido, aunque menos ilustrativo.
#!/usr/bin/env bash
# Recursive repeat() to build separator lines dynamically
awk '
function repeat(s, n, acc) {
if (n <= 0) return ""
acc = s repeat(s, n-1)
return acc
}
BEGIN {
header = sprintf("%-15s %10s %10s", "Metric", "Current", "Target")
sep = repeat("-", length(header))
print sep
print header
print sep
printf "%-15s %10.1f %10.1f\n", "CPU %", 72.4, 60.0
printf "%-15s %10.1f %10.1f\n", "Mem GB", 14.2, 16.0
printf "%-15s %10d %10d\n", "Open FDs", 1024, 800
print sep
}
' /dev/nullprintf en un archivo o una tubería dentro de awk
El printf (y print) de awk puede redirigir la salida a archivos o enviarla mediante una tubería a comandos del shell, directamente desde el programa awk y sin envolver todo en un subshell.
printf "..." > "file.txt"— escribe en un archivo (lo trunca una vez y después añade contenido durante la misma ejecución).printf "..." >> "file.txt"— añade contenido a un archivo.printf "..." | "sort -rn"— envía la salida mediante una tubería a un comando del shell; awk mantiene la tubería abierta entre registros y la cierra al finalizar el programa.
Un patrón habitual consiste en dividir una sola pasada por un registro en varios archivos de salida, uno por estado o por host, para evitar recorrer repetidamente un archivo grande.
#!/usr/bin/env bash
# Route records into per-status files using print redirection
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
printf '200 /index.html\n404 /missing\n200 /api/data\n500 /crash\n404 /lost\n' | awk -v out="$tmpdir" '
{
file = out "/" $1 ".log"
printf "%-6s %s\n", $1, $2 > file
}
END {
close(file) # flush all open handles
}
'
echo "=== 200 ==="
cat "$tmpdir/200.log"
echo "=== 404 ==="
cat "$tmpdir/404.log"Generación de un informe CSV con awk
No todos los informes están destinados a las personas. A veces, la salida debe ser un CSV legible por máquinas que alimente una hoja de cálculo o una canalización posterior. El printf de awk lo gestiona de forma sencilla: establezca OFS como una coma o controle cada delimitador explícitamente.
Dos reglas importantes para generar CSV robustos con awk:
- Rodee con comillas dobles los campos que puedan contener comas o saltos de línea.
- Escape las comillas dobles literales dentro de un campo duplicándolas:
He said ""hello"".
La función csv_field(s) encapsula esta lógica de entrecomillado para poder aplicarla de forma coherente a todos los campos de texto.
#!/usr/bin/env bash
# Emit a valid CSV report from space-separated input
printf 'alice engineer 95000\nbob "sales,mgr" 82000\ncarol developer 110000\n' | awk '
function csv_field(s, safe) {
safe = s
gsub(/"/, "\"\"" , safe) # double any embedded quotes
return "\"" safe "\"" # wrap in quotes
}
BEGIN { print "Name,Role,Salary" }
{
printf "%s,%s,%d\n", csv_field($1), csv_field($2), $3
}
'De principio a fin: un informe completo del registro de acceso
Este ejemplo final reúne todos los conceptos: funciones definidas por el usuario, formato con printf, acumulación en arrays y una cabecera y un pie estructurados. La entrada es un registro simplificado de acceso web; la salida es una tabla de resumen legible para personas, con totales por método y una línea con el total general.
Técnicas principales utilizadas:
count[method]++ybytes[method] += sizeacumulan las estadísticas de cada método en una sola pasada.human_bytes()(de una escena anterior) convierte los totales de bytes.- Usar
printfcon anchos coincidentes en la cabecera, los datos y el pie garantiza que la tabla permanezca alineada independientemente del tamaño de la entrada. - El bloque
ENDrecorre el array confor (k in arr)y genera la salida ordenada mediante una tubería haciasort.
#!/usr/bin/env bash
# Full access log report: method breakdown with human-readable bytes
printf 'GET /index 200 4096\nPOST /api 201 512\nGET /img 200 102400\nDELETE /item 204 0\nPOST /login 401 256\nGET /style 200 8192\n' | awk '
function human_bytes(n, s) {
if (n >= 1048576) s = sprintf("%.1f MB", n/1048576)
else if (n >= 1024) s = sprintf("%.1f KB", n/1024)
else s = sprintf("%d B", n)
return s
}
{
method = $1
size = $4
count[method]++
bytes[method] += size
grand_count++
grand_bytes += size
}
END {
fmt = "%-10s %8s %15s\n"
sep = "----------------------------------"
printf fmt, "Method", "Requests", "Bytes"
print sep
for (m in count)
printf "%-10s %8d %15s\n", m, count[m], human_bytes(bytes[m])
print sep
printf "%-10s %8d %15s\n", "TOTAL", grand_count, human_bytes(grand_bytes)
}
'Comprobación de conocimientos: variables locales en funciones de awk
¿Cuál de las siguientes opciones declara correctamente result como una variable local dentro de una función de awk, siguiendo la convención POSIX?
Repaso de la lección: funciones de awk, printf y generación de informes
En esta lección ha aprendido a escribir programas awk de calidad profesional que generan informes con formato a partir de flujos de datos sin procesar. Esto es lo que debe recordar:
- Las funciones definidas por el usuario se declaran con
function name(params, locals). Los espacios adicionales antes de los parámetros de variables locales son la convención POSIX; no existe ninguna palabra clavelocal. - printf proporciona un control preciso de la salida: ancho (
%10s), alineación (%-10s) y precisión (%8.2f). Recuerde añadir\nexplícitamente. - Use BEGIN para generar cabeceras y separadores, las reglas por registro para acumular datos y END para calcular los totales e imprimir el informe terminado.
- Redirija la salida de
printfa archivos (> file) o canalizaciones (| "sort") desde awk para dividir informes o procesar posteriormente la salida. - Rodee los campos de texto con un ayudante csv_field() al generar CSV para gestionar de forma segura las comas y las comillas incrustadas.
- Conviene conservar en una biblioteca personal de awk las funciones que convierten unidades (
human_bytes), repiten caracteres (repeat) o limpian cadenas (trim,csv_field): se combinan fácilmente entre proyectos.
Preguntas frecuentes
¿La lección «Funciones de awk, formato con printf y generación de informes» es gratis?
Sí — el texto completo de «Funciones de awk, formato con printf y generación de informes» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.
¿Qué aprenderé en «Funciones de awk, formato con printf y generación de informes»?
Defina funciones propias y use printf para generar informes tabulares bien presentados a partir de flujos de datos sin procesar. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?
No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.
¿Cuánto tiempo toma la lección «Funciones de awk, formato con printf y generación de informes»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?
Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Registros, campos y separadores personalizados en awk
- Patrones, rangos y bloques BEGIN/END
- Agregación con arrays y agrupación en awk
- Funciones de awk, formato con printf y generación de informes