Redirecciones avanzadas en Bash: stderr, combinación de flujos, tee y manipulación de descriptores
Introducción
En el día a día de un desarrollador o administrador de sistemas, trabajar con la línea de comandos implica constantemente capturar, filtrar y redirigir la salida de los programas. Aunque la mayoría conoce los operadores básicos como > y >>, existen técnicas más poderosas que permiten controlar precisamente qué flujos de datos van a cada destino, duplicarlos o incluso reordenarlos. Este tutorial está dirigido a quienes ya tienen nociones de shell y desean profundizar en el manejo de descriptores de archivo, aprender a redirigir únicamente el flujo de error estándar (stderr), combinar stdout y stderr en un solo flujo, utilizar tee para guardar y mostrar la salida simultáneamente, y jugar con los descriptores de archivo (fd) para crear redirecciones más sofisticadas.
Al finalizar, sabrás:
- Cómo redirigir solo
stderra un archivo mientras dejasstdouten la terminal. - Cómo unir ambos flujos con
&>y variantes como|&. - Cómo usar
teepara duplicar la salida a varios destinos. - Cómo manipular descriptores de archivo con
exec, duplicarlos (&>), cerrarlos y reasignarlos. - Buenas prácticas para evitar pérdida de información y errores comunes.
Conceptos básicos de descriptores de archivo
En Unix/Linux, cada proceso tiene asociados varios descriptores de archivo (fd) que representan canales de entrada/salida. Los más importantes son:
0: stdin (entrada estándar).1: stdout (salida estándar).2: stderr (salida de error estándar).
Los operadores de redirección actúan sobre estos números. Por ejemplo, 2> error.log le dice al shell que envíe todo lo que el proceso escriba en el descriptor 2 al archivo error.log. Si no se especifica un número, se asume 1 (stdout).
Redirigir solo stderr (2>)
Supongamos que tienes un script que genera tanto información útil como mensajes de error y quieres guardar únicamente esos errores para analizarlos posteriormente, sin ensuciar la salida normal.
# Simulamos un programa que escribe en stdout y stderr
mi_comando() {
echo "Esto va a stdout"
echo "Esto va a stderr" >&2
echo "Más stdout"
echo "Otro error" >&2
}
# Ejecutamos y redirigimos stderr a un archivo
mi_comando 2> errores.txt
Salida esperada en la terminal:
Esto va a stdout
Más stdout
Contenido de errores.txt después de la ejecución:
Esto va a stderr
Otro error
Este enfoque permite procesar los errores por separado: por ejemplo, podrías enviar el archivo a un sistema de monitoreo o aplicar filtros como grep.
Redirigir stdout a un archivo y dejar stderr en pantalla
La inversión también es útil: guardar la salida normal y ver los errores en tiempo real.
mi_comando > salida.txt
Terminal:
Esto va a stderr
Otro error
salida.txt:
Esto va a stdout
Más stdout
Redirigir ambos flujos a archivos diferentes
Puedes combinar las dos redirecciones en una sola línea:
mi_comando > salida.txt 2> errores.txt
Ahora salida.txt contendrá solo lo que iba a stdout y errores.txt solo lo que iba a stderr.
Combinar stdout y stderr (&> y |&)
En muchas situaciones deseas tratar ambas salidas como un único flujo, por ejemplo para enviarlas a un pipe o a un registro unificado.
Operador &> (Bash 4+)
El operador &> redirige tanto stdout como stderr al mismo destino.
mi_comando &> todo.txt
Contenido de todo.txt:
Esto va a stdout
Esto va a stderr
Más stdout
Otro error
El orden de las líneas depende del timing interno del proceso; en la práctica suele ser el orden en que fueron escritas.
Operador |& (pipe que incluye stderr)
Cuando quieres pasar la salida combinada a otro comando mediante un pipe, Bash ofrece |&, equivalente a 2>&1 |.
mi_comando |& grep -i stdout
Esto filtrará únicamente las líneas que contengan la palabra "stdout" (ignorando mayúsculas/minúsculas) del flujo combinado.
Salida esperada:
Esto va a stdout
Más stdout
Redirección manual con 2>&1
Si tu shell es POSIX y no soporta &>, puedes lograr lo mismo con:
mi_comando 2>&1 > combinado.txt
Nota el orden: primero duplicamos stderr a donde apunte stdout (2>&1), luego redirigimos stdout (que ahora lleva también el stderr) al archivo. Si inviertes el orden (> combinado.txt 2>&1) obtendrías solo stdout.
Duplicar salida con tee
El comando tee lee desde su entrada estándar y escribe simultáneamente a uno o más archivos y a suyos archivos. Es ideal cuando deseas guardar el registro.
Uso básico
mi_comando | tee registro | tee registro.log
Todo lo que mi_comando envía a stdoutstdout(pantalla) y se copiará aregistro.log. Si el proceso también aparecerá en la terminal y quedará guardado en registro.log`.
Duplicar a varios archivos
tee acepta varios nombres de archivo:
mi_comando | tee salida1.txt salida2.txt
Ahora ambas copias reciben idéntico contenido.
Añadir (-a) en lugar de sobrescribir
Por defecto tee sobrescribe los archivos. Para añadir al final usa -a:
mi_comando | tee -a historial.log
Redirigir stderr también a tee
Si quieres capturar tanto stdout como stderr con tee, primero combina los flujos y luego pásalos a tee:
mi_comando 2>&1 | tee todo.txt
Ahora todo.txt contendrá todo lo que el proceso escribió, tanto normal como de error.
Ejemplo práctico: registro de compilación
Imagina que estás compilando un proyecto grande y deseas ver el progreso en pantalla mientras guardas un registro completo para depuración futura.
make 2>&1 | tee build.log
Durante la compilación verás advertencias y errores en la terminal, y al final tendrás build.log con el mismo contenido.
Manipulación avanzada de descriptores de archivo
Más allá de los operadores de redirección simples, Bash permite manipular descriptores de archivo directamente con el builtin exec. Esto abre posibilidades como:
- Duplicar un descriptor en otro (
>&). - Cerrar un descriptor (
<&-). - Redirigir un descriptor a otro archivo o a
/dev/null. - Crear descriptores personalizados (por ejemplo,
3,4, …) para uso temporal.
Duplicar descriptores
Supongamos que deseas guardar una copia de stdout antes de redirigirlo a un archivo, para luego restaurarla.
# Guardar copia de fd 1 en fd 3
exec 3>&1
# Redirigir stdout a un archivo
exec > salida.txt
# Ahora cualquier echo irá a salida.txt
echo "Esto va al archivo"
# Restaurar stdout desde la copia guardada
exec 1>&3
# Cerrar el descriptor temporal
exec 3>&-
# Volver a la terminal
echo "Esto vuelve a la pantalla"
Salida en terminal:
Esto vuelve a la pantalla
Contenido de salida.txt:
Esto va al archivo
Redirigir stderr a un descriptor personalizado
Puedes asignar stderr a un descriptor distinto, manipularlo y luego volverlo a unir.
# fd 4 apunta a un archivo temporal
exec 4> errores.tmp
# Redirigir stderr a fd 4
exec 2>&4
# Ejecutar comando que produce errores
mi_comando
# Restaurar stderr original
exec 2>&-
# Cerrar fd 4
exec 4>&-
# Mostrar lo capturado
cat errores.tmp
Salida de cat errores.tmp:
Esto va a stderr
Otro error
Suprimir selectivamente flujos
A veces quieres ejecutar un comando pero ignorar completamente su salida o sus errores.
- Descartar
stdout:comando >/dev/null - Descartar
stderr:comando 2>/dev/null - Descartar ambos:
comando &>/dev/nullocomando >/dev/null 2>&1
Ejemplo: ejecutar una prueba que suele generar advertencias ruidosas pero que solo te importa el código de salida.
if ./test_suite >/dev/null 2>&1; then
echo "Prueba aprobada"
else
echo "Prueba falló"
fi
Usar descriptores para crear tuberías internas
Bash permite crear tuberías anónimas con <() y >(), que bajo el hood usan descriptores de archivo. Esto es útil para procesar la salida de un comando sin crear archivos intermedios.
Example: comparar salidas de dos comandos
diff <(cmd1) <(cmd2)
Aquí Bash crea dos tuberías, asigna cada una a un descriptor de archivo y las pasa como argumentos a diff.
Example: tee a un proceso
cmd | tee >(grep -i error > errores.log) >(wc -l > cuenta.txt)
En este caso, tee duplica su entrada a tres destinos: la terminal estándar (por defecto) y dos procesos de sustitución que filtran errores y cuentan líneas.
Buenas prácticas y errores comunes
1. Orden de las redirecciones importa
Recuerda que las redirecciones se procesan de izquierda a derecha. Un error frecuente es escribir:
comando 2>&1 > salida.txt # MAL: solo stdout va al archivo
La secuencia correcta es:
comando > salida.txt 2>&1 # Bien: ambos van al archivo
2. No confundir &> con >&
&>redirige tanto stdout como stderr a un archivo.>&Nduplica el descriptor N a donde apunte actualmente stdout (o viceversa según contexto).
3. tee y buffers
Cuando usas tee en una tubería larga, la salida puede aparecer tardía debido al buffering. Forzar el vaciado inmediato se puede hacer con stdbuf o usando la opción --line-buffered de grep cuando sea relevante.
comando | stdbuf -oL tee salida.txt
4. Cerrar descriptores innecesarios
En scripts largos, dejar descriptores abiertos puede consumir recursos o causar comportamientos inesperados (por ejemplo, un proceso hijo heredando un fd que debería estar cerrado). Siempre que duplicas un descriptor con exec, considera cerrarlo cuando ya no lo necesites:
exec 3>&1
# ... usar fd 3 ...
exec 3>&-
5. Evitar redirecciones bucles
Redirigir un descriptor a sí mismo o crear un bucle de tuberías puede bloquear la shell. Por ejemplo:
comando >&0 # peligroso: stdin ahora apunta a stdout del mismo comando
6. Registros rotativos
Si vas a usar tee para mantener un registro que crece indefinidamente, considera rotado con herramientas como logrotate o implementa tu propia lógica de truncado.
comando | tee -a registro.log
# luego, cada noche, truncar si supera cierto tamaño
Conclusión
Dominar las redirecciones avanzadas en Bash te brinda un control fino sobre cómo fluye la información entre procesos, archivos y la terminal. Has visto cómo:
- Aislar y capturar únicamente
stderrcon2>. - Unir
stdoutystderrmediante&>o2>&1. - Duplicar la salida en tiempo real con
tee, incluyendo la opción de añadir y dirigir a múltiples destinos. - Manipular directamente descriptores de archivo usando
exec, duplicarlos, cerrarlos y crear canales temporales para necesidades específicas.
Aplicando estas técnicas podrás construir scripts más limpios, registros más útiles y tuberías más eficientes, evitando la pérdida de información y reduciendo el ruido innecesario. Practica los ejemplos, adapta los patrones a tus casos de uso y recuerda siempre verificar el orden de las redirecciones y cerrar descriptores que ya no necesites.
Con este conocimiento, estarás preparado para enfrentar cualquier escenario de línea de comandos donde la precisión en la gestión de flujos sea crucial.
¿Te quedó alguna duda? Deja un comentario y con gusto te ayudo a profundir en algún punto.
DUGLAS MORENO
Comentarios (0)
Sé el primero en comentar.