Módulo 3: Tuberías, redirección y composición
2. La filosofía Unix: herramientas chicas que se combinan
Descripción
Al terminar esta lección vas a poder explicar por qué casi ningún programa serio de la terminal intenta hacerlo todo, y vas a tener un criterio concreto para decidir si un problema nuevo se resuelve combinando comandos que ya conoces en lugar de salir a instalar algo distinto. No es una curiosidad histórica: es el filtro mental que separa a quien memorizó cincuenta comandos de quien realmente entiende para qué sirve la terminal.
Ese criterio se paga solo la primera vez que lo aplicas en el trabajo real. Alguien que analiza datos y necesita saber cuántas filas de un archivo de diez millones de líneas tienen un valor vacío en cierta columna no abre una hoja de cálculo ni escribe un script de treinta líneas: combina dos o tres comandos que ya existen en cualquier máquina Linux. Un agente de IA que ejecuta comandos en una terminal —como los que hoy automatizan tareas de código— hace exactamente lo mismo: no tiene una integración especial programada para cada herramienta, tiene acceso a una terminal y a programas que leen y escriben texto plano. Por eso puede combinar find, grep y wc sin que nadie haya escrito jamás código para conectar esos tres programas entre sí.
Conexión con el módulo: la lección anterior te mostró una tubería funcionando como un truco de magia, sin explicar por qué era posible. Esta lección te da la razón de fondo: no es una casualidad de sintaxis, es una decisión de diseño de hace casi cincuenta años que hizo que los programas hablaran todos el mismo idioma. Las dos lecciones siguientes —los tres flujos y la redirección— te van a mostrar el mecanismo técnico exacto que hace posible esa idea.
Una ferretería, no una fábrica todo-en-uno
Piensa en una ferretería de verdad, no en el comando que lleva ese nombre. En la sección de plomería hay llaves de paso, codos, uniones en T, reducciones, válvulas: docenas de piezas chicas, cada una resolviendo un solo problema (cerrar el flujo, cambiar de dirección, dividir en dos ramales). Ningún fabricante vende una sola pieza que sea simultáneamente llave, codo y válvula. Y sin embargo puedes comprar un codo de una marca y una válvula de otra completamente distinta, y encajan sin ajustar nada, porque ambas respetan la misma medida de rosca estándar. Esa medida estándar es lo que permite construir instalaciones que nadie diseñó como un todo: cada fabricante solo necesita cumplir el estándar, no conocer las piezas de los demás.
Unix se construyó con la misma apuesta, y no es casualidad de vocabulario: el símbolo | que ya usaste en la lección anterior se llama justamente "tubería" ("pipe") porque hace lo mismo que un codo de plomería —conecta una pieza con otra sin que ninguna de las dos sepa nada de la otra— y la "medida de rosca estándar" de Unix es el texto plano: líneas de caracteres legibles, sin ningún formato binario propietario de por medio. Mientras un programa lea texto por su entrada y escriba texto por su salida, puedes conectarlo con cualquier otro programa que respete el mismo trato, sin importar quién lo escribió, en qué año ni en qué lenguaje.
En 1978, en el prefacio del número especial que la revista técnica de Bell Labs dedicó a Unix, Doug McIlroy —quien encabezaba el centro de investigación de Bell Labs donde nació Unix, y que ya en 1964 había propuesto la idea de las tuberías, la misma que Ken Thompson terminaría implementando en 1973— resumió la idea en tres frases que se siguen citando en su idioma original, sin cambiar una palabra:
«Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.»
Traducido: escribe programas que hagan una sola cosa y la hagan bien; escribe programas que trabajen juntos; escribe programas que manejen flujos de texto, porque esa es una interfaz universal. Son tres decisiones, no una, y ninguna funciona sola. Programas chicos y enfocados (grep busca, no cuenta ni ordena). Programas que se combinan en vez de competir por hacer de todo. Y texto plano como el idioma común que hace posible la combinación: si grep hiciera su trabajo perfectamente pero escribiera un formato binario propio, no podrías conectarlo con nada de lo que ya conoces.
Ejemplo trabajado
Vas a reproducir esto en tu propia terminal; no hace falta ningún archivo especial, solo lo que ya aprendiste en el módulo 2.
# creamos una carpeta de práctica con archivos vacíos: solo nos interesan los nombres
mkdir -p ~/unix-philosophy-practice
cd ~/unix-philosophy-practice
touch notes.txt draft.md README.md report.md ideas.txt config.yaml
Ahora respondamos una pregunta concreta: ¿cuántos archivos Markdown (.md) hay en esta carpeta?
find sabe localizar archivos por patrón, pero no sabe contar:
find . -name "*.md"
Qué esperar:
./README.md
./draft.md
./report.md
Tienes la lista, no el número. Y wc —la herramienta del módulo 2 que cuenta líneas, palabras y caracteres— no sabe buscar nada por sí sola: si le pasas el nombre de una carpeta, no tiene forma de decidir qué archivos te interesan. Ninguna de las dos, por separado, contesta la pregunta completa. Conéctalas:
find . -name "*.md" | wc -l
Qué esperar:
3
Nada cambió en el disco y no instalaste ninguna herramienta nueva. find sigue haciendo exactamente una cosa (localizar) y wc sigue haciendo exactamente una cosa (contar); la pregunta compuesta —"¿cuántos archivos de este tipo hay?"— la resuelve la combinación, no un tercer programa que alguien tuvo que escribir a medida para ese caso específico. Esto es la filosofía funcionando en la práctica, no una idea abstracta.
El criterio antes que la herramienta
El reflejo más caro en la terminal no es escribir mal un comando: es no preguntarte, antes de instalar o programar algo, si lo que ya tienes alcanza. El criterio es simple y conviene aplicarlo en este orden:
- Aísla la pregunta atómica. No "necesito un analizador de logs", sino "¿cuántas líneas cumplen esta condición exacta?".
- Revisa qué comandos ya conoces que resuelven un pedazo. Casi siempre hay uno que filtra (
grep,find) y uno que cuenta o transforma (wc, y más adelantesort,cut). - Conecta esos pedazos antes de salir a buscar algo nuevo. Si la combinación existe, no necesitas nada más.
Aplícalo a un caso real. Un compañero de equipo te dice: "necesito una herramienta que me diga cuántas líneas de este log mencionan WARNING pero no mencionan ignored, para saber cuántas advertencias reales quedan sin resolver". Antes de buscar "log analyzer" en internet, aplica el criterio: la pregunta atómica es un conteo condicional; grep ya filtra por patrón y ya sabe invertir la búsqueda con -v (módulo 2); wc -l ya cuenta líneas. Encadenados:
grep "WARNING" server.log | grep -v "ignored" | wc -l
Dos filtros y un conteo, ningún programa nuevo. Instalar una herramienta dedicada solo se justifica cuando la combinación deja de alcanzar —por ejemplo, cuando la condición ya no es sobre texto plano, que es exactamente el límite que viene ahora.
Dónde el texto plano se queda corto
La filosofía no dice "todo es texto y con eso basta"; dice que el texto plano es la interfaz universal para datos que se leen línea por línea. Eso tiene un límite real, y conviene decirlo con la misma honestidad con la que se dice la ventaja.
Datos con estructura anidada. Un archivo JSON es texto, pero no es texto línea por línea: un mismo objeto puede estar en una sola línea larguísima o repartido en cincuenta, y el orden de sus campos no está garantizado. grep busca patrones de caracteres, no conceptos; si buscas la palabra email en un JSON, tanto puede encontrar el valor que necesitas como la clave "email": en un lugar que no te interesa, o no encontrar nada si el archivo viene comprimido en una sola línea larga. Para esto existe jq: un programa que en lugar de leer caracteres lee la estructura (objetos, listas, claves) y te deja preguntar "¿qué usuarios tienen el campo "active": true?" en sus propios términos. Vale la pena notar algo: jq no rompe la filosofía, la extiende —sigue leyendo texto por su entrada, sigue escribiendo texto por su salida, sigue combinándose con tuberías igual que grep— solo que entiende una estructura más rica que una simple línea.
Delimitadores que mienten. Un archivo CSV parece texto plano perfecto para cut -d, -f2, hasta que un campo trae una coma adentro de sus propias comillas ("Doe, John",42,Lima). cut no sabe de comillas: cuenta comas a ciegas y te entrega la columna equivocada, sin ningún error que te avise. Ahí el texto sigue siendo texto, pero el delimitador dejó de ser confiable; hace falta una herramienta que entienda el formato CSV de verdad (por ejemplo csvkit), no un corte por posición de carácter.
Datos binarios. Una imagen, un ejecutable compilado o un archivo de base de datos SQLite no tienen "líneas" en ningún sentido útil. Pasarlos por grep o cat no produce un error educado: produce caracteres ilegibles en tu pantalla o, en el peor caso, una terminal que necesitas reiniciar. Esos formatos tienen sus propias herramientas especializadas (un editor de imágenes, un depurador, un cliente SQL), y forzarlos a la tubería de texto no es fidelidad a la filosofía: es ignorar el límite que la filosofía misma declara.
Errores comunes
Creer que "seguir la filosofía Unix" significa usar solo comandos de los años setenta. Qué pasa: alguien rechaza herramientas modernas como ripgrep o jq argumentando que "no son Unix de verdad", y se queda pegado a versiones más lentas o menos cómodas de la misma idea. Por qué pasa: la filosofía describe un comportamiento —una sola cosa, texto plano, composición—, no una fecha de nacimiento. ripgrep busca patrones, lee y escribe texto, se combina con tuberías igual que grep; solo que además respeta .gitignore y es más rápido. Es tan fiel a la filosofía como el original, o más. Cómo detectarlo: si tu argumento para evitar una herramienta es "es demasiado nueva" y no "hace más de una cosa" o "no habla texto plano", el argumento no tiene que ver con la filosofía sino con costumbre. Cómo corregirlo: evalúa cualquier herramienta, vieja o nueva, con las tres preguntas de McIlroy —¿hace una cosa?, ¿habla texto?, ¿se combina?— y no con su año de lanzamiento.
Escribir un script desde cero, o instalar un paquete, para un problema que ya resuelven dos comandos conectados. Qué pasa: inviertes veinte minutos escribiendo y depurando un script de treinta líneas —o agregas una dependencia nueva a un proyecto— para algo que find ... | wc -l o grep ... | grep -v ... resuelven en una sola línea. Por qué pasa: falta el hábito de aplicar el criterio de la sección anterior antes de teclear la primera línea de código; el reflejo por defecto es "programar" en lugar de "combinar". Cómo detectarlo: si tu solución tiene más líneas que la pregunta que respondía, o si agregaste una dependencia solo para un conteo o un filtro puntual, probablemente había una tubería más corta esperando. Cómo corregirlo: antes de abrir un editor, escribe en una línea cuál es la pregunta atómica que necesitas responder y revisa si dos comandos que ya conoces la resuelven encadenados.
Usar cut o grep sobre un formato que no es texto plano línea por línea, y confiar en el resultado sin verificarlo. Qué pasa: cut -d, -f2 sobre un CSV con un campo entrecomillado que contiene una coma te entrega el pedazo equivocado de un campo, y el comando no falla: solo miente en silencio. Por qué pasa: cut cuenta el carácter delimitador de forma literal, sin ningún conocimiento de comillas ni de la estructura del formato; hace exactamente lo que se le pidió, que no era lo que hacía falta. Cómo detectarlo: los valores de una columna no cuadran con lo que esperabas, o aparecen desplazados justo en las filas que tienen el delimitador incrustado dentro de un campo. Cómo corregirlo: si el archivo es CSV real, usa una herramienta que entienda comillas y estructura (csvkit u otra); si es JSON, usa jq. Reserva cut para columnas separadas por un delimitador simple y sin excepciones, que es exactamente el caso para el que se diseñó.
Ejercicios
Ejercicio 1
Reusa la carpeta ~/unix-philosophy-practice del ejemplo trabajado (o crea una nueva con mkdir y touch) y agrega estos archivos: access.log, error.log, debug.log, main.py, utils.py. Sin usar ninguna herramienta que "cuente archivos por tipo", responde con un solo comando cada pregunta: ¿cuántos archivos .log hay? ¿Cuántos archivos NO son .py?
Ver solución
find . -type f -name "*.log" | wc -l
find . -type f ! -name "*.py" | wc -l
Por qué funciona: find filtra por nombre o por negación de nombre (!, ya visto en el módulo 2), y no sabe contar; wc -l cuenta líneas de lo que recibe, y no sabe buscar nada por sí solo. Cada pregunta se resuelve conectando el filtro correcto con el conteo, sin que exista ni haga falta un tercer comando "contador de archivos por extensión".
Ejercicio 2
Un compañero de equipo necesita saber cuántos archivos de configuración (.yaml o .yml) hay en un proyecto, sin contar los que están dentro de la carpeta tests. Aplica el criterio de esta lección: ¿qué combinación de comandos ya conocidos responde esto sin instalar nada?
Ver solución
find . \( -name "*.yaml" -o -name "*.yml" \) | grep -v "/tests/" | wc -l
Por qué funciona: find con -o (ya visto en el módulo 2) localiza ambas extensiones; grep -v descarta las rutas que contienen /tests/; wc -l cuenta lo que queda. Tres herramientas, cada una haciendo exactamente lo suyo, encadenadas para responder una pregunta que ninguna resuelve sola.
Ejercicio 3
Tienes un archivo de configuración en formato JSON con una lista de usuarios, cada uno con campos como email y active. Quieres extraer solo los correos electrónicos de los usuarios activos. ¿Por qué grep y cut no son la herramienta adecuada acá, aunque el archivo sea "texto"? ¿Qué usarías en su lugar?
Ver solución
grep busca patrones de caracteres, no conceptos: buscar email puede devolver tanto el valor que necesitas como el nombre del campo ("email":) en un registro que no te interesa, y no tiene forma de aplicar la condición "solo si active es true" porque esa condición vive en otra parte del mismo objeto. cut depende de un delimitador de posición fija, y JSON no tiene columnas: un mismo dato puede estar en distintas posiciones según el orden en que se escribieron las claves. La herramienta adecuada es jq, que entiende la estructura del JSON (objetos, listas, claves anidadas) y te permite expresar la condición directamente: algo como jq '.[] | select(.active == true) | .email'.
Por qué funciona: JSON es un formato con estructura anidada, no texto línea por línea; jq fue diseñado para leer esa estructura en lugar de caracteres sueltos, que es exactamente el límite que esta lección marcó para grep y cut.
Ejercicio 4
¿Cuál de estas dos afirmaciones es más fiel a la filosofía Unix: (a) "nunca instales una herramienta que no exista desde los años setenta", o (b) "antes de instalar algo nuevo, pregúntate si conectar lo que ya tienes resuelve el problema"? Justifica tu respuesta con un ejemplo de una herramienta moderna que sí respeta la filosofía.
Ver solución
La (b). La filosofía de McIlroy nunca habla de antigüedad; habla de comportamiento: hacer una cosa bien, hablar texto plano, combinarse con otros programas. Una herramienta creada en 2026 que cumple esas tres condiciones es tan "Unix" como una de 1978. ripgrep, por ejemplo, es mucho más reciente que grep, pero busca patrones (una sola cosa), lee y escribe texto plano, y se combina en tuberías exactamente igual que su antecesor; solo que además respeta .gitignore por defecto y es notablemente más rápido en proyectos grandes.
Por qué funciona: confundir la filosofía con nostalgia es exactamente el primer error conceptual de esta lección; separar "comportamiento" de "año de creación" es lo que evita caer en él.
Resumen y siguiente paso
Ya sabes por qué la terminal se comporta como un lenguaje y no como una colección de aplicaciones aisladas: cada programa hace una cosa, todos hablan texto plano, y la potencia real vive en la combinación. Tienes también un criterio concreto —aislar la pregunta atómica, buscar qué pedazo resuelve cada comando que ya conoces, combinar antes de instalar— y sabes dónde ese enfoque deja de alcanzar: datos anidados como JSON, delimitadores que mienten y binarios que no tienen líneas.
Lo que todavía no viste es el mecanismo técnico exacto que hace posible conectar dos programas: qué es lo que un programa "escribe" cuando escribe texto, y por qué un error sigue apareciendo en tu pantalla aunque hayas redirigido la salida a un archivo. Eso es exactamente la siguiente lección: los tres flujos que tiene todo proceso desde el momento en que nace.
Antes de avanzar deberías poder explicar, con tus propias palabras y sin mirar esta lección, por qué grep y wc pueden combinarse aunque los haya escrito gente distinta en épocas distintas, y aplicar el criterio de esta lección frente a un problema nuevo antes de instalar cualquier cosa.
Recursos
- UNIX Time-Sharing System: Foreword — el número especial de 1978 de la Bell System Technical Journal donde Doug McIlroy escribió la formulación original de la filosofía citada en esta lección.
- The Art of Unix Programming — capítulo 1 — el desarrollo completo de Eric S. Raymond sobre los principios de diseño de Unix, con ejemplos históricos y contemporáneos.
- Program Design in the Unix Environment — el artículo de Rob Pike y Brian Kernighan (1983) que muestra con ejemplos reales de código cómo se diseña un programa pensado para combinarse con otros.
- Manual oficial de jq — referencia completa de la herramienta que extiende la filosofía a datos JSON estructurados.
- ripgrep en GitHub — el
READMEdel proyecto explica en detalle por qué una herramienta moderna sigue respetando (y en algunos casos mejorando) los principios de 1978.