1. Qué es redirigir
Redirigir es conectar uno de los canales estándar de un comando a un archivo, en lugar de la terminal. La conexión la hace la shell antes de ejecutar el comando, que no se entera del cambio.
comando > salida.txt 2> errores.txt: cada canal de salida, a su archivo. Nada llega a la pantalla.Todos los operadores del tema responden a un mismo esquema: un número de canal, que a veces se omite; un signo que indica la dirección; y un destino.
| Signo | Dirección | Canal, si no se indica |
|---|---|---|
> | Del comando hacia un archivo. | El 1, la salida. |
< | De un archivo hacia el comando. | El 0, la entrada. |
El signo apunta en el sentido en que viajan los datos. Trabajamos en un taller dentro del directorio de práctica:
$ cd ~/practica
$ mkdir taller
$ cd taller
2. > : la salida a un archivo
El operador más usado envía a un archivo lo que el comando iba a escribir en la pantalla:
$ ls -l .. > listado.txt
$ cat listado.txt
total 32
drwxrwxr-x 2 ana ana 4096 oct 12 10:40 fotos
-rw-rw-r-- 1 ana ana 12813 oct 12 10:41 informe.txt
-rw-rw-r-- 1 ana ana 18 oct 12 10:32 notas.txt
drwxrwxr-x 2 ana ana 4096 oct 12 19:30 taller
-rw-rw-r-- 1 ana ana 70 oct 12 13:20 tareas.txt
El primer comando no mostró nada: todo lo que ls escribió fue a parar al archivo. Si listado.txt no existía, se creó; el nombre de la derecha es siempre un archivo, nunca un comando.
Así se conserva cualquier resultado: un listado, la salida de id, la lista de paquetes instalados, el resultado de una búsqueda con find. Lo que antes pasaba por la pantalla y se perdía, queda guardado y se puede revisar, comparar o enviar.
Lo usamos desde el tema 2, sin explicarlo, para crear archivos: echo "Mi primer archivo" > notas.txt. Ahora se entiende qué ocurría. echo no sabe crear archivos; escribe en su salida, y la shell había conectado esa salida a un archivo.
3. > vacía el archivo antes de empezar
El operador tiene un comportamiento que hay que conocer antes de usarlo en serio:
Si el archivo de destino existe, > lo vacía y escribe en él desde cero. Lo que contenía se pierde. No hay aviso ni confirmación, y el comando ni siquiera interviene: lo hace la shell. Es, junto con rm y cp, la tercera manera de destruir datos con una línea.
$ echo "uno" > prueba.txt
$ echo "dos" > prueba.txt
$ cat prueba.txt
dos
El momento en que ocurre es decisivo. Según el orden que vimos en el tema 22, la shell abre los archivos antes de lanzar el comando. Dos experimentos lo muestran. El primero es inofensivo:
$ ls > contenido.txt
$ cat contenido.txt
contenido.txt
listado.txt
prueba.txt
contenido.txt figura dentro de su propio listado. Cuando ls empezó a trabajar, el archivo ya había sido creado por la shell.
El segundo experimento es la trampa clásica. Se quiere ordenar un archivo y guardar el resultado en el mismo archivo. Lo probamos sobre una copia:
$ cp ../tareas.txt .
$ sort tareas.txt > tareas.txt # «ordenar el archivo en su lugar»
$ wc -c tareas.txt
0 tareas.txt
El archivo quedó vacío. La shell lo truncó para preparar la salida, y cuando sort lo abrió para leerlo ya no había nada que ordenar. No hubo ningún mensaje de error, porque desde el punto de vista de cada parte todo salió bien.
La forma segura es escribir en un archivo distinto y, si hace falta, renombrarlo después: sort tareas.txt > ordenadas.txt. Algunos comandos tienen una opción propia para escribir sobre su entrada sin este problema, como sort -o.
$ cp ../tareas.txt . # recuperar la copia
$ sort tareas.txt > ordenadas.txt
$ cat ordenadas.txt
Comprar pan
Estudiar vim
Llamar a Luis
Pagar la luz
Regar las plantas
Para quien quiera una red de protección, Bash tiene una opción que impide que > sobrescriba archivos existentes:
$ set -o noclobber
$ echo "tres" > prueba.txt
bash: prueba.txt: cannot overwrite existing file
$ echo "tres" >| prueba.txt # >| : sobrescribir de todos modos
$ set +o noclobber # volver al comportamiento normal
4. >> : añadir al final
Con el signo doble, la salida se agrega al final del archivo, y lo que había se conserva:
$ echo "Inicio de la tarea" >> registro.log
$ echo "Fin de la tarea" >> registro.log
$ cat registro.log
Inicio de la tarea
Fin de la tarea
> | >> | |
|---|---|---|
| Si el archivo no existe | Lo crea. | Lo crea. |
| Si existe | Lo vacía y escribe. | Escribe a continuación. |
| Para | Un resultado que reemplaza al anterior. | Un registro que se acumula. |
| Riesgo | Perder el contenido previo. | Ninguno para lo existente. |
Es el operador de los registros, y el que usamos en el tema 12 para alimentar un archivo mientras tail -f lo seguía. También es la forma habitual de añadir una línea a un archivo de configuración sin abrir un editor, como un alias en ~/.bash_aliases.
Ante la duda entre los dos, >> es el que no destruye. El error contrario —usar > donde se quería añadir, sobre un archivo de configuración— es de los más frecuentes y de los más caros: deja el archivo con una sola línea.
5. 2> : los errores a un archivo
Los dos operadores anteriores actúan sobre el canal 1. Para el canal de errores se antepone su número, sin espacio entre el 2 y el signo:
$ ls ../notas.txt nada.txt 2> errores.txt
../notas.txt
$ cat errores.txt
ls: cannot access 'nada.txt': No such file or directory
El resultado siguió llegando a la pantalla, y el mensaje de error quedó en el archivo. Los dos canales se pueden redirigir a la vez, cada uno a su destino, que es la situación del diagrama:
$ find /etc -name "*.conf" > conf.txt 2> sin-permiso.txt
$ wc -l conf.txt sin-permiso.txt
312 conf.txt
4 sin-permiso.txt
316 total
La búsqueda no escribió nada en la pantalla. Quedaron dos archivos: uno con los 312 resultados, limpio, listo para procesar; y otro con los cuatro directorios que find no pudo revisar, para mirarlos después. Es la separación entre datos y comentarios del tema anterior, llevada al disco.
| Operador | Efecto |
|---|---|
> archivo, o 1> archivo | La salida, reemplazando. |
>> archivo | La salida, añadiendo. |
2> archivo | Los errores, reemplazando. |
2>> archivo | Los errores, añadiendo. |
El número debe ir pegado al signo. Con un espacio, 2 > archivo, la shell entiende otra cosa: un argumento 2 para el comando, y una redirección común de la salida.
6. Juntar los dos canales: 2>&1
A veces se quiere lo contrario de separar: todo lo que el comando diga, resultados y errores, en un solo archivo y en el orden en que ocurrió. Es lo habitual al guardar el registro de una tarea larga.
El intento obvio, > todo.txt 2> todo.txt, no funciona bien: la shell abre el archivo dos veces, de manera independiente, y lo que se escribe por un canal pisa lo que se escribe por el otro. Hace falta decir que los dos canales comparten un mismo destino:
comando > todo.txt 2>&1
│││
││└─ …del canal 1
│└─ &: «lo que sigue es un canal, no un archivo»: el destino actual…
└─ el canal 2 pasa a apuntar al…
Se lee: «el canal 2, envíalo al mismo lugar adonde está yendo el canal 1». El signo & es lo que distingue un número de canal de un nombre de archivo: 2>1, sin él, enviaría los errores a un archivo llamado 1.
$ ls ../notas.txt nada.txt > todo.txt 2>&1
$ cat todo.txt
ls: cannot access 'nada.txt': No such file or directory
../notas.txt
Bash tiene una abreviatura para esta combinación, más corta y sin la dificultad de la sección siguiente:
| Forma abreviada | Equivale a | Efecto |
|---|---|---|
&> archivo | > archivo 2>&1 | Salida y errores al archivo, reemplazando. |
&>> archivo | >> archivo 2>&1 | Salida y errores al archivo, añadiendo. |
La forma abreviada es propia de Bash. La larga funciona en todas las shells, y es la que encontrarás en scripts pensados para cualquier sistema; por eso conviene saber leer las dos.
7. El orden importa
La forma larga tiene una exigencia que desconcierta: 2>&1 debe escribirse después de la redirección de la salida. Al revés, no hace lo esperado:
$ ls ../notas.txt nada.txt 2>&1 > todo.txt
ls: cannot access 'nada.txt': No such file or directory
$ cat todo.txt
../notas.txt
El error apareció en la pantalla, y el archivo solo recibió el resultado. La explicación está en dos hechos. La shell procesa las redirecciones de izquierda a derecha. Y 2>&1 no crea un vínculo permanente entre los canales: copia el destino que el canal 1 tiene en ese instante.
> todo.txt 2>&1 | 2>&1 > todo.txt | |
|---|---|---|
| Al comenzar | 1 → pantalla, 2 → pantalla | 1 → pantalla, 2 → pantalla |
| Primera redirección | > todo.txt1 → archivo | 2>&12 → donde está el 1: la pantalla |
| Segunda redirección | 2>&12 → donde está el 1: el archivo | > todo.txt1 → archivo |
| Resultado | 1 → archivo, 2 → archivo | 1 → archivo, 2 → pantalla |
En el segundo caso, cuando se copia el destino del canal 1, este todavía apunta a la pantalla. El cambio posterior del canal 1 ya no afecta al 2.
La regla práctica: primero se dice adónde va la salida, y después que los errores la acompañan. O se usa &>, que no deja lugar a equivocarse.
8. Descartar: /dev/null
Cuando lo que un comando escribe no interesa, el destino es el archivo de dispositivo que conocimos en el tema 8: /dev/null acepta todo lo que se le envía y no guarda nada.
| Se escribe | Se descarta | Se usa para |
|---|---|---|
comando 2> /dev/null | Los errores. | Quitar avisos previsibles y quedarse con los resultados. El caso más común. |
comando > /dev/null | Los resultados. | Ver solo si hubo errores. |
comando &> /dev/null | Todo. | Ejecutar en silencio, cuando solo importa si tuvo éxito. |
El tercer caso parece absurdo: ¿para qué ejecutar un comando y tirar todo lo que dice? Porque queda la cuarta vía del tema anterior, el código de salida. Muchas veces la pregunta no es «¿qué encontraste?» sino «¿encontraste algo?»:
$ grep "pan" tareas.txt &> /dev/null
$ echo $?
0 # 0: la palabra está en el archivo
Es la forma en que los scripts hacen preguntas a los comandos; la aprovechamos en el tema 25.
2> /dev/null es adecuado cuando se sabe qué mensajes vendrán y por qué no importan, como los de permisos en una búsqueda. Puesto por costumbre, para que la pantalla quede limpia, oculta justo la información que hace falta el día que algo falla. Si un comando no funciona y lleva esa redirección, lo primero es quitarla.
9. < : la entrada desde un archivo
El signo invertido conecta un archivo al canal 0: el comando recibe por su entrada el contenido del archivo, como si alguien lo estuviera tecleando.
$ tr 'a-z' 'A-Z' < ../notas.txt
MI PRIMER ARCHIVO
$ sort < tareas.txt > ordenadas.txt # la entrada de un archivo, la salida a otro
tr, de translate, reemplaza unos caracteres por otros; aquí, las minúsculas por mayúsculas. Lo vemos en el tema 26. Sirve de ejemplo porque es uno de los pocos comandos que solo lee de su entrada: no acepta un nombre de archivo, de modo que < es la manera de darle uno.
Con la mayoría de los comandos, < es opcional, porque aceptan el archivo como argumento. Las diferencias las vimos en el tema anterior, con wc. Resumidas:
comando archivo | comando < archivo | |
|---|---|---|
| Abre el archivo | El comando | La shell |
| El comando conoce el nombre | Sí | No |
| Si el archivo no existe | El comando informa el error. | La shell informa el error, y el comando no se ejecuta. |
| Varios archivos | Sí | Uno solo |
La línea sort < tareas.txt > ordenadas.txt se lee de izquierda a derecha como un recorrido: los datos salen del primer archivo, pasan por sort y llegan al segundo.
10. Documentos en línea: <<
Hay una forma de darle a un comando varias líneas de entrada sin crear un archivo: escribirlas a continuación, en la misma línea de comandos o en el mismo script.
Un documento en línea, en inglés here document, es un bloque de texto que la shell entrega a la entrada estándar de un comando. Empieza con << seguido de una palabra, y termina en la primera línea que contenga solo esa palabra.
$ cat > config.txt << FIN
> usuario=$USER
> directorio=$HOME
> FIN
$ cat config.txt
usuario=ana
directorio=/home/ana
El indicador > al comienzo de cada renglón lo pone la shell, como vimos en el tema 5: avisa que espera el resto. La palabra FIN es un delimitador que se elige libremente; por costumbre se usa EOF. La línea de cierre debe contener esa palabra y nada más, sin espacios antes ni después.
La primera línea combina dos redirecciones: la entrada de cat es el bloque de texto, y su salida va a config.txt. Es la manera corriente de crear un archivo de varias líneas desde un script, y la que verás en muchas guías de instalación.
Dentro del bloque, la shell reemplazó las variables, como entre comillas dobles. Poniendo el delimitador entre comillas se desactiva, y el texto llega literal:
| Se escribe | Dentro del bloque | Equivale a |
|---|---|---|
<< FIN | Las variables y las sustituciones se expanden. | Comillas dobles. |
<< 'FIN' | Todo es literal. | Comillas simples. |
<<- FIN | Se quitan las tabulaciones iniciales de cada línea. | Permite sangrar el bloque en un script. |
11. Cadenas en línea: <<<
Para una sola línea hay una forma todavía más breve, con tres signos. El texto que sigue se entrega a la entrada del comando:
$ tr 'a-z' 'A-Z' <<< "hola mundo"
HOLA MUNDO
$ wc -w <<< "uno dos tres"
3
Reemplaza a una construcción muy común, echo "texto" | comando, con el mismo resultado. Es propia de Bash, y resulta cómoda para probar un filtro con un dato suelto, que es justo como la usaremos en el bloque 6.
| La entrada viene de… | Operador | Ejemplo |
|---|---|---|
| Un archivo | < | sort < lista.txt |
| Varias líneas escritas a continuación | << | sort << FIN |
| Un texto de una línea | <<< | sort <<< "texto" |
| Otro comando | | | ls | sort — tema 24 |
12. Dónde se escribe una redirección
Las redirecciones no son argumentos. La shell las reconoce, las aplica y las quita de la línea antes de pasarla al comando. De eso se siguen varias reglas de escritura.
Pueden ir en cualquier lugar de la línea. Las tres formas siguientes son idénticas, y en las tres ls recibe los mismos dos argumentos:
ls -l .. > listado.txt
ls -l > listado.txt ..
> listado.txt ls -l ..
Por legibilidad se escriben al final, y esa es la costumbre que conviene seguir.
El espacio después del signo es opcional; antes del número, obligatorio. >archivo y > archivo son lo mismo. En cambio 2> no admite un espacio en el medio, y debe estar separado de lo que tenga delante.
Una redirección sin comando también vale. Como el archivo lo crea o lo vacía la shell, no hace falta ningún comando para eso:
$ > vacio.txt # crea el archivo, o lo vacía si existía
$ ls -l vacio.txt
-rw-rw-r-- 1 ana ana 0 oct 12 19:52 vacio.txt
Es el modo habitual de vaciar un registro que creció demasiado sin borrarlo, cosa que importa cuando un servicio lo tiene abierto, por lo que explicamos en el tema 14.
Cada canal va a un solo destino. Con comando > a.txt > b.txt se crean los dos archivos y la salida va solo al último; el primero queda vacío. Para enviar la misma salida a dos lugares hace falta un comando, tee, que vemos en el tema siguiente.
13. Redirecciones y permisos
Como el archivo lo abre la shell, lo abre con tu identidad y con los permisos que estudiamos en el bloque 4:
| Operación | Requiere |
|---|---|
> o >> sobre un archivo que existe | Permiso w sobre el archivo. |
> o >> sobre un archivo nuevo | Permisos w y x sobre el directorio. |
< | Permiso r sobre el archivo. |
$ echo "prueba" > /etc/prueba.conf
bash: /etc/prueba.conf: Permission denied
El mensaje empieza con bash, no con echo: quien no pudo abrir el archivo fue la shell, y echo no llegó a ejecutarse. Es lo que encontramos en el tema 19, y la razón por la que sudo delante no lo arregla. La solución que vimos allí, | sudo tee, consiste en que el archivo lo abra un programa que sí corre con privilegios.
Una observación sobre lo que se crea: los archivos nacidos de una redirección reciben los permisos que resultan de la máscara, rw-rw-r-- en Ubuntu. Si lo que se guarda es delicado, conviene fijar antes la máscara o ajustar después los permisos, como vimos en el tema 18.
14. Todos los operadores
| Operador | Canal | Efecto |
|---|---|---|
> archivo | 1 | La salida al archivo, reemplazando el contenido. |
>> archivo | 1 | La salida al archivo, añadiendo al final. |
2> archivo | 2 | Los errores al archivo, reemplazando. |
2>> archivo | 2 | Los errores al archivo, añadiendo. |
&> archivo | 1 y 2 | Salida y errores al archivo, reemplazando. |
&>> archivo | 1 y 2 | Salida y errores al archivo, añadiendo. |
2>&1 | 2 | Los errores, al destino actual de la salida. |
>&2, o 1>&2 | 1 | La salida, al destino actual de los errores. |
>| archivo | 1 | Reemplaza aunque esté activa la opción noclobber. |
< archivo | 0 | La entrada desde el archivo. |
<< PALABRA | 0 | La entrada desde las líneas siguientes, hasta PALABRA. |
<<< "texto" | 0 | La entrada desde ese texto. |
La octava fila es nueva, y tiene un uso preciso. Es lo que hace un script para escribir un mensaje de error por el canal que corresponde: echo "No se encontró el archivo" >&2. Así, quien use ese script podrá separar sus resultados de sus errores, como hicimos aquí con los de ls y find.
Y las recetas de todos los días:
| Quiero… | Comando |
|---|---|
| Guardar el resultado de un comando | comando > resultado.txt |
| Ir acumulando resultados | comando >> registro.txt |
| Quitar los mensajes de error de la pantalla | comando 2> /dev/null |
| Guardar todo lo que diga una tarea | comando &> tarea.log |
| Guardar aparte lo que salió mal | comando > ok.txt 2> errores.txt |
| Saber solo si funcionó | comando &> /dev/null, y mirar $? |
| Vaciar un archivo sin borrarlo | > archivo |
| Añadir una línea a un archivo | echo "línea" >> archivo |
| Crear un archivo de varias líneas | cat > archivo << FIN |
Desarmamos el taller:
$ cd ~/practica
$ rm -r taller
$ ls
fotos informe.txt notas.txt tareas.txt
Con las redirecciones, los canales de un comando llegan a los archivos. En el tema 24 damos el paso que falta: conectar la salida de un comando directamente con la entrada de otro, sin archivos de por medio.
15. Resumen
- Redirigir es conectar un canal de un comando a un archivo. Lo hace la shell, antes de ejecutar el comando.
>envía la salida a un archivo. Si el archivo existe, lo vacía primero, sin preguntar.- Como la shell prepara el archivo antes de lanzar el comando, usar el mismo archivo como entrada y como salida lo deja vacío.
>>añade al final del archivo, conservando lo que había.2>y2>>hacen lo mismo con el canal de errores. El número va pegado al signo.2>&1envía los errores al destino actual de la salida.&>es la abreviatura de Bash para enviar los dos canales a un archivo.- Las redirecciones se procesan de izquierda a derecha:
> archivo 2>&1junta los canales; en el orden inverso, los errores siguen yendo a la pantalla. /dev/nulldescarta lo que recibe.2> /dev/nullquita los errores;&> /dev/null, todo, y queda el código de salida.<conecta un archivo a la entrada del comando.<< PALABRAentrega como entrada las líneas siguientes, hasta el delimitador;<<<, un texto de una línea.- Las redirecciones no son argumentos: pueden ir en cualquier lugar de la línea, y el comando no las recibe.
- El archivo de una redirección se abre con la identidad y los permisos del usuario, no con los del comando.
16. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. ¿Qué hace ls -l > listado.txt si listado.txt ya existe?
Reemplaza su contenido por el listado. Lo que el archivo contenía se pierde, sin aviso.
2. ¿Qué diferencia hay entre > y >>?
> vacía el archivo y escribe desde el principio. >> escribe a continuación de lo que ya había.
3. ¿Por qué sort datos.txt > datos.txt deja el archivo vacío?
Porque la shell vacía el archivo de destino antes de ejecutar sort. Cuando sort lo abre para leer, ya no tiene contenido.
4. ¿Qué hace la opción noclobber?
Impide que > sobrescriba un archivo existente. Se activa con set -o noclobber, y se puede forzar un reemplazo con >|.
5. ¿Cómo se guardan en un archivo solo los mensajes de error de un comando?
Con comando 2> errores.txt. Los resultados siguen saliendo por la pantalla.
6. ¿Qué diferencia hay entre 2> archivo y 2 > archivo?
La primera redirige el canal de errores. En la segunda, el espacio hace que el 2 sea un argumento del comando, y lo que se redirige es la salida.
7. ¿Qué significa 2>&1?
Que el canal 2, los errores, pase a ir al mismo destino al que está yendo en ese momento el canal 1, la salida.
8. ¿Qué hace comando > todo.log 2>&1, y cuál es su forma abreviada?
Envía la salida y los errores al archivo todo.log. En Bash se abrevia comando &> todo.log.
9. ¿Por qué comando 2>&1 > todo.log no guarda los errores en el archivo?
Porque las redirecciones se aplican de izquierda a derecha. Cuando se procesa 2>&1, el canal 1 todavía apunta a la pantalla, y ese destino es el que se copia. El cambio posterior del canal 1 no afecta al 2.
10. ¿Para qué sirve comando &> /dev/null?
Para ejecutar el comando sin que escriba nada, cuando lo único que interesa es su código de salida.
11. ¿Qué riesgo tiene agregar 2> /dev/null por costumbre?
Que se dejan de ver los mensajes de error, que son la información necesaria para saber por qué algo no funciona.
12. ¿Qué diferencia hay entre wc -l datos.txt y wc -l < datos.txt cuando el archivo no existe?
En el primer caso, wc se ejecuta e informa el error. En el segundo, la shell no puede abrir el archivo, informa ella el error, y wc no llega a ejecutarse.
13. ¿Qué es un documento en línea y cómo se termina?
Un bloque de texto que se entrega a la entrada de un comando. Empieza con << y una palabra, y termina en la línea que contiene solo esa palabra.
14. ¿Qué diferencia hay entre << FIN y << 'FIN'?
Con el delimitador sin comillas, las variables y sustituciones del bloque se expanden. Con comillas, el texto se entrega literal.
15. ¿Por qué falla sudo echo "x" > /etc/archivo?
Porque el archivo lo abre la shell del usuario, sin privilegios, antes de que sudo se ejecute. El comando privilegiado es solo echo.
Para practicar
- Guarda en un archivo la salida de
id, en otro la dels -la ~, y añade al primero la dedate. Revisa los dos concat. - Ejecuta dos veces
date > fecha.txty dos vecesdate >> fechas.txt. ¿Cuántas líneas tiene cada archivo? - Reproduce la trampa de la sección 3 con una copia de un archivo, y luego ordénala correctamente de dos maneras: con un archivo intermedio y con
sort -o. - Ejecuta
find /var -name "*.log"guardando los resultados en un archivo y los errores en otro. Cuenta las líneas de cada uno. - Repite la búsqueda enviando todo a un solo archivo, primero con
> archivo 2>&1y luego con&>. Compara los dos archivos conwc -l. - Prueba
ls nada.txt 2>&1 > salida.txtyls nada.txt > salida.txt 2>&1. ¿En cuál queda el error dentro del archivo? - Activa
noclobber, intenta sobrescribir un archivo, fuérzalo con>|, y desactiva la opción. - Crea con un documento en línea un archivo de cinco líneas que incluya
$HOMEy$(date). Repite con el delimitador entre comillas simples y compara. - Usa
tr 'a-z' 'A-Z'con<sobretareas.txt, y con<<<sobre un texto cualquiera. Guarda el primer resultado en un archivo. - Crea un archivo con contenido, vacíalo con
> archivoy comprueba conls -li, antes y después, que el inodo es el mismo.