La filosofía Unix: herramientas pequeñas y pipes

Cuando Dennis Ritchie y Ken Thompson diseñaron Unix en los laboratorios Bell a finales de los años 60, tomaron una decisión de diseño que parece obvia en retrospectiva pero que era radical en su época: cada programa hace una sola cosa, y la hace bien. ls lista archivos. sort ordena líneas. grep filtra texto. Ninguno de los tres sabe que los otros existen, y sin embargo los tres pueden colaborar.

El mecanismo que lo hace posible se llama pipe (tubería), representado por el carácter |. Un pipe conecta la salida estándar (stdout) de un programa directamente con la entrada estándar (stdin) del siguiente. El sistema operativo gestiona ese canal en memoria: el primer programa escribe, el segundo lee, sin que ningún fichero temporal toque el disco. Lo que fluye por ese canal es siempre lo mismo: texto plano, una línea por elemento. Ese contrato tan simple —texto plano, una línea— es lo que hace que herramientas escritas por personas distintas, en décadas distintas, encajen perfectamente entre sí.

¿Cuándo usas esto? Constantemente, aunque al principio no lo parezca. Cada vez que buscas un proceso específico, filtras logs, ordenas resultados o cuentas ocurrencias, estás componiendo herramientas. La alternativa —un único programa monolítico que lista, filtra, ordena y cuenta— existía antes de Unix y existe hoy en algunos entornos gráficos. El problema es que si ese programa no admite exactamente la combinación que necesitas, estás atascado. Con pipes, si conoces cinco herramientas independientes, tienes potencialmente cinco factorial combinaciones posibles. En la práctica, el espacio de lo que puedes hacer crece más rápido que lo que nadie podría enseñarte explícitamente.

Lo que se rompe cuando ignoras esta filosofía es la composabilidad. Si escribes un script que hace demasiado internamente —que lee, filtra, transforma y escribe en un solo bloque sin leer de stdin ni escribir a stdout— ese script no puede participar en una cadena de pipes. Queda aislado. A pequeña escala parece irrelevante; en producción, cuando necesitas encadenarlo con algo que aún no has escrito, lamentas no haberlo diseñado de otra forma.

Un ejemplo concreto: los cinco últimos logs de error

Imagina que tienes un directorio con muchos ficheros de log y quieres ver los cinco ficheros más recientes cuyo nombre contiene la palabra error. Sin pipes necesitarías un programa específico para eso, o escribir código. Con pipes:

# Listamos con marcas de tiempo para que sort pueda ordenar por fecha
ls -lt /var/log/ \
  | grep "error" \
  | head -5

Vamos a expandir el escenario para ver más herramientas trabajando juntas. Queremos saber cuáles son las cinco direcciones IP que más veces aparecen en el log de autenticación:

# grep extrae solo las líneas que contienen una dirección IPv4
# El patrón '[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' captura IPs sin depender de herramientas externas
grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' /var/log/auth.log \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -5

Si /var/log/auth.log tiene entradas reales de intentos de conexión, la salida tiene esta pinta:

    342 192.168.1.105
     87 10.0.0.23
     61 203.0.113.47
     14 198.51.100.9
      8 172.16.0.1

Lo que está pasando línea a línea

grep -oE '...' usa la opción -o para imprimir solo la parte que coincide, no la línea completa. Sin -o, cada línea del log completo pasaría al siguiente comando y sort no podría agrupar correctamente. La opción -E activa expresiones regulares extendidas, que tienen una sintaxis más limpia para este patrón.

El primer sort no tiene ninguna opción porque aquí solo nos importa que las IPs iguales queden juntas. uniq -c cuenta cuántas veces aparece cada línea consecutiva idéntica —por eso necesita que el input esté ordenado previamente; si no lo estuviera, contaría cada bloque por separado y los resultados serían incorrectos.

El segundo sort -rn ordena numéricamente (-n) en orden descendente (-r). Sin -n, sort ordenaría lexicográficamente y 342 quedaría antes que 87 por casualidad, pero 9 quedaría antes que 87 porque "9" > "8" carácter a carácter.

head -5 cierra la cadena cogiendo solo las cinco primeras líneas. Si lo cambias por tail -5 ves las cinco IPs menos frecuentes. Si lo eliminas completamente, ves todas. Cada modificación cuesta un segundo y no requiere reescribir nada.

Fíjate en algo importante: ninguna de estas herramientas sabe lo que están haciendo las otras. sort no sabe que viene de grep. uniq no sabe que va hacia otro sort. Cada una lee de stdin, escribe a stdout, y el kernel conecta los extremos. Cuando aprendes awk, sed, cut o jq, no aprendes herramientas nuevas en un vacío —aprendes piezas que encajan en el mismo sistema de conexiones que ya conoces.

8

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio