← Terminal y Línea de Comandos en Linux Bloque 5 · Redirecciones y tuberías · Tema 23 de 43

Tema 23

Redirigir la entrada y la salida

En el tema anterior vimos que un comando tiene tres canales y que, si nadie dice otra cosa, los tres van a la terminal. Decir otra cosa es redirigir. Con unos pocos signos se guarda en un archivo lo que un comando iba a mostrar, se aparta lo que salió mal de lo que salió bien, se descarta lo que no interesa o se le da a un programa un archivo en lugar del teclado. Son operadores breves, de uso constante, y uno de ellos puede vaciar un archivo en silencio: conviene conocerlos con exactitud.

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.

Un comando con sus dos salidas redirigidas: la salida estándar, canal 1, va al archivo salida.txt; el error estándar, canal 2, va al archivo errores.txt; la entrada sigue viniendo del teclado 0 1 > 2 > Teclado Comando no sabe nada salida.txt errores.txt
El efecto de 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.

SignoDirecciónCanal, 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:

terminal
$ 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:

terminal, en ~/practica/taller
$ 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:

> reemplaza el contenido del archivo, sin preguntar

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.

terminal
$ 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:

terminal: el archivo existe antes de que ls se ejecute
$ 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:

terminal: leer y escribir el mismo archivo
$ 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.

Nunca el mismo archivo como origen y como destino

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.

terminal: la forma correcta
$ 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:

terminal
$ 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:

terminal
$ 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 existeLo crea.Lo crea.
Si existeLo vacía y escribe.Escribe a continuación.
ParaUn resultado que reemplaza al anterior.Un registro que se acumula.
RiesgoPerder 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:

terminal
$ 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:

terminal (los números varían)
$ 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.

OperadorEfecto
> archivo, o 1> archivoLa salida, reemplazando.
>> archivoLa salida, añadiendo.
2> archivoLos errores, reemplazando.
2>> archivoLos 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:

anatomía de 2>&1
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.

terminal
$ 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 abreviadaEquivale aEfecto
&> archivo> archivo 2>&1Salida y errores al archivo, reemplazando.
&>> archivo>> archivo 2>&1Salida 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:

terminal: el mismo comando, con las redirecciones en el otro orden
$ 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>&12>&1 > todo.txt
Al comenzar1 → pantalla, 2 → pantalla1 → pantalla, 2 → pantalla
Primera redirección> todo.txt
1 → archivo
2>&1
2 → donde está el 1: la pantalla
Segunda redirección2>&1
2 → donde está el 1: el archivo
> todo.txt
1 → archivo
Resultado1 → archivo, 2 → archivo1 → 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 escribeSe descartaSe usa para
comando 2> /dev/nullLos errores.Quitar avisos previsibles y quedarse con los resultados. El caso más común.
comando > /dev/nullLos resultados.Ver solo si hubo errores.
comando &> /dev/nullTodo.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?»:

terminal
$ 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.

Descartar errores es dejar de verlos

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.

terminal
$ 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 archivocomando < archivo
Abre el archivoEl comandoLa shell
El comando conoce el nombreSíNo
Si el archivo no existeEl comando informa el error.La shell informa el error, y el comando no se ejecuta.
Varios archivosSí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.

terminal
$ 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 escribeDentro del bloqueEquivale a
<< FINLas variables y las sustituciones se expanden.Comillas dobles.
<< 'FIN'Todo es literal.Comillas simples.
<<- FINSe 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:

terminal
$ 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…OperadorEjemplo
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:

tres formas equivalentes
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:

terminal
$ > 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ónRequiere
> o >> sobre un archivo que existePermiso w sobre el archivo.
> o >> sobre un archivo nuevoPermisos w y x sobre el directorio.
<Permiso r sobre el archivo.
terminal
$ 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

OperadorCanalEfecto
> archivo1La salida al archivo, reemplazando el contenido.
>> archivo1La salida al archivo, añadiendo al final.
2> archivo2Los errores al archivo, reemplazando.
2>> archivo2Los errores al archivo, añadiendo.
&> archivo1 y 2Salida y errores al archivo, reemplazando.
&>> archivo1 y 2Salida y errores al archivo, añadiendo.
2>&12Los errores, al destino actual de la salida.
>&2, o 1>&21La salida, al destino actual de los errores.
>| archivo1Reemplaza aunque esté activa la opción noclobber.
< archivo0La entrada desde el archivo.
<< PALABRA0La entrada desde las líneas siguientes, hasta PALABRA.
<<< "texto"0La 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 comandocomando > resultado.txt
Ir acumulando resultadoscomando >> registro.txt
Quitar los mensajes de error de la pantallacomando 2> /dev/null
Guardar todo lo que diga una tareacomando &> tarea.log
Guardar aparte lo que salió malcomando > 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 archivoecho "línea" >> archivo
Crear un archivo de varias líneascat > archivo << FIN

Desarmamos el taller:

terminal
$ 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

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

  1. Guarda en un archivo la salida de id, en otro la de ls -la ~, y añade al primero la de date. Revisa los dos con cat.
  2. Ejecuta dos veces date > fecha.txt y dos veces date >> fechas.txt. ¿Cuántas líneas tiene cada archivo?
  3. 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.
  4. Ejecuta find /var -name "*.log" guardando los resultados en un archivo y los errores en otro. Cuenta las líneas de cada uno.
  5. Repite la búsqueda enviando todo a un solo archivo, primero con > archivo 2>&1 y luego con &>. Compara los dos archivos con wc -l.
  6. Prueba ls nada.txt 2>&1 > salida.txt y ls nada.txt > salida.txt 2>&1. ¿En cuál queda el error dentro del archivo?
  7. Activa noclobber, intenta sobrescribir un archivo, fuérzalo con >|, y desactiva la opción.
  8. Crea con un documento en línea un archivo de cinco líneas que incluya $HOME y $(date). Repite con el delimitador entre comillas simples y compara.
  9. Usa tr 'a-z' 'A-Z' con < sobre tareas.txt, y con <<< sobre un texto cualquiera. Guarda el primer resultado en un archivo.
  10. Crea un archivo con contenido, vacíalo con > archivo y comprueba con ls -li, antes y después, que el inodo es el mismo.