Cuando un proceso arranca en Linux, el sistema operativo le entrega tres canales de comunicación ya abiertos, sin que el programa tenga que pedirlos. Se llaman stdin, stdout y stderr, y están identificados por los números 0, 1 y 2 respectivamente. Son file descriptors, es decir, referencias numéricas a recursos de I/O que el kernel gestiona por cada proceso. No son ficheros en disco, aunque se comporten como si lo fueran: son abstracciones que pueden apuntar al teclado, a la pantalla, a un pipe, a un socket, o a cualquier cosa que el sistema operativo sepa leer o escribir.
El diseño de tres canales separados no es arbitrario. stdin (fd 0) es la entrada: por defecto apunta al teclado, y el proceso lee de ahí cuando espera datos del usuario o de otro programa. stdout (fd 1) es la salida normal: por defecto apunta a la terminal, y el proceso escribe ahí sus resultados, sus datos, lo que quiere comunicar hacia afuera. stderr (fd 2) es la salida de errores y diagnóstico: también apunta a la terminal por defecto, pero es un canal completamente independiente de stdout.
Esa independencia es el punto clave. Si stdout y stderr fueran el mismo canal, no podrías encadenar programas de forma fiable. Cuando escribes programa_a | programa_b, el shell conecta el stdout de programa_a al stdin de programa_b. Si programa_a mezcla errores con datos en el mismo stream, programa_b recibiría basura entre los datos útiles. Al tener stderr separado, los mensajes de error van directamente a tu terminal sin contaminar el pipe. stdout es para datos. stderr es para mensajes al operador.
¿Cuándo te importa saber esto? En cuanto empieces a redirigir salida, a encadenar comandos con pipes, o a escribir scripts que otros programas van a consumir. Si no entiendes que un mensaje de error aparece en pantalla aunque hayas redirigido la salida a un fichero, pasarás un buen rato desconcertado. Y si mezclas los dos streams sin querer en un script, el programa siguiente en la cadena leerá mensajes de error como si fueran datos válidos.
# Ejecutamos un comando que produce salida normal Y un error # ls lista el directorio actual (stdout) y falla con el que no existe (stderr) ls /tmp /directorio_que_no_existe # Para ver que son canales distintos, redirigimos solo stdout a un fichero # El error sigue apareciendo en la terminal porque stderr no está redirigido ls /tmp /directorio_que_no_existe > /tmp/salida_normal.txt # Comprobamos qué capturó el fichero: solo los datos de stdout cat /tmp/salida_normal.txt # Ahora redirigimos solo stderr a otro fichero # La salida normal aparece en la terminal; el error va al fichero ls /tmp /directorio_que_no_existe 2> /tmp/solo_errores.txt # Verificamos el fichero de errores cat /tmp/solo_errores.txt # echo siempre escribe en stdout echo "este mensaje va a stdout" # Para escribir en stderr desde bash hay que redirigir explícitamente al fd 2 echo "este es un error" >&2
Fíjate en lo que pasa con el primer ls: ves tanto la lista de /tmp como el mensaje de error, y visualmente parecen lo mismo porque los dos van a la terminal. Esa ilusión es exactamente lo que lleva a confusión. El shell los está escribiendo por separado, en canales distintos, pero tu emulador de terminal los mezcla en pantalla porque ambos terminan apuntando al mismo destino físico.
Cuando añades > /tmp/salida_normal.txt, le dices al shell que conecte el fd 1 de ls a ese fichero. El fd 2 no se toca: sigue apuntando a la terminal. Por eso el error aparece en pantalla aunque hayas redirigido la salida. No es que la redirección falle, es que redirigiste el canal correcto y el error viaja por el canal que no tocaste.
La sintaxis 2> hace lo mismo pero para el fd 2: conecta stderr al fichero indicado, dejando stdout intacto apuntando a la terminal. El número antes del > es literalmente el número del file descriptor.
La última línea del ejemplo, echo "este es un error" >&2, muestra cómo un script puede escribir deliberadamente en stderr. La notación >&2 significa “redirige mi stdout al mismo destino al que apunta el fd 2”. En un script bien escrito, los mensajes de diagnóstico, advertencias y errores van a stderr, y los datos que otro programa va a consumir van a stdout. Así cualquiera que use tu script puede separar unos de otros sin adivinar nada.
N° 40