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

Tema 22

Entrada, salida y errores estándar

Varias veces escribimos cosas como | less, > archivo o 2>/dev/null, y funcionaron. Lo hicimos como quien repite una fórmula. Detrás hay una de las ideas más fecundas de Unix, y es de una sencillez desarmante: todo programa lee de un lugar y escribe en otro, sin saber cuáles son. Este tema explica ese modelo, que es breve y conceptual, y sobre el que se construye todo el bloque: las redirecciones, las tuberías y la manera de combinar comandos pequeños para resolver problemas grandes.

1. Un comando es una caja con tres bocas

Hasta ahora miramos los comandos desde afuera: qué opciones aceptan, qué hacen con los archivos. Para este bloque conviene mirarlos de otra manera, como los ve el sistema. Un programa en ejecución es una caja que recibe datos por un lado y entrega datos por otro.

Lo notable es lo que el programa no sabe. sort ordena las líneas que le llegan y escribe el resultado. No sabe si esas líneas vienen del teclado, de un archivo o de otro programa. No sabe si lo que escribe aparecerá en una pantalla, quedará guardado en el disco o será la entrada de un tercero. Lee de «su entrada» y escribe en «su salida», y nada más.

Esa ignorancia es deliberada, y es la clave de todo. Como ningún programa depende de dónde vienen sus datos ni adónde van, cualquiera de ellos se puede conectar con cualquier otro, con un archivo o con la terminal, sin modificarlo. Quien hace las conexiones es la shell.

2. Los tres canales estándar

Cada proceso nace con tres canales de comunicación ya abiertos, listos para usar:

Los tres canales de un comando: recibe datos del teclado por la entrada estándar, de número 0; y envía a la pantalla sus resultados por la salida estándar, de número 1, y sus mensajes de error por el error estándar, de número 2 0 · entrada 1 · salida 2 · errores Teclado Comando un proceso Pantalla
La situación por defecto: la entrada viene del teclado, y las dos salidas van a la pantalla, donde se mezclan.
CanalNombre en inglésAbreviaturaNúmeroPara qué se usaConectado por defecto a
Entrada estándarstandard inputstdin0Los datos que el programa debe procesar.El teclado.
Salida estándarstandard outputstdout1Los resultados.La pantalla.
Error estándarstandard errorstderr2Los mensajes de error y de aviso.La pantalla.

«Estándar» significa aquí «el que se usa si no se indica otro». Las abreviaturas stdin, stdout y stderr aparecen en toda la documentación y en casi todos los lenguajes de programación, porque el modelo salió de Unix y fue adoptado en todas partes.

3. Los descriptores 0, 1 y 2

Los números de la tabla no son una convención de este curso. Son la forma en que el kernel identifica cada canal.

Un descriptor de archivo es un número entero con el que un proceso se refiere a un archivo, o a cualquier otra cosa que se pueda leer o escribir, que tiene abierto. Cada proceso tiene su propia tabla de descriptores.

Cuando un programa abre un archivo, el kernel le entrega un número: el primero que tenga libre. A partir de ahí, el programa lee y escribe indicando ese número. Los tres primeros están reservados por costumbre universal, y todo proceso los recibe ya abiertos al nacer:

DescriptorCanalEl programa lo usa para
0Entrada estándarLeer.
1Salida estándarEscribir resultados.
2Error estándarEscribir mensajes.
3 en adelante—Los archivos que el propio programa abra.

Esto explica una escritura que usamos desde el tema 10. En 2>/dev/null, el 2 es el descriptor: «el canal número 2, envíalo a…». Los operadores de redirección se refieren a los canales por su número.

En el tema 8 vimos que /proc expone los datos de cada proceso. Entre ellos está su tabla de descriptores, que se puede listar:

terminal
$ ls -l /proc/self/fd
total 0
lrwx------ 1 ana ana 64 oct 12 19:10 0 -> /dev/pts/0
lrwx------ 1 ana ana 64 oct 12 19:10 1 -> /dev/pts/0
lrwx------ 1 ana ana 64 oct 12 19:10 2 -> /dev/pts/0
lr-x------ 1 ana ana 64 oct 12 19:10 3 -> /proc/5321/fd

Es la tabla del propio ls, que al leer /proc/self se mira a sí mismo. Tiene cuatro descriptores. Los tres estándar apuntan a /dev/pts/0; el cuarto, el 3, es el directorio que ls abrió para listarlo, y es el primero que el programa pidió por su cuenta.

4. Por defecto, la terminal

El listado anterior muestra a qué están conectados los canales cuando no se indica nada: los tres al mismo archivo, /dev/pts/0. Es el archivo de dispositivo de la terminal, como informa el comando tty:

terminal
$ tty
/dev/pts/0

Un solo archivo sirve para todo porque una terminal es un dispositivo de dos direcciones. Leer de él entrega lo que se escribe en el teclado; escribir en él hace aparecer texto en la ventana. «Teclado» y «pantalla» son las dos caras del mismo archivo.

De aquí sale una observación que conviene retener: los canales no se inventan para cada comando. La shell tiene los suyos, conectados a la terminal desde que se abrió la ventana, y cada comando que lanza los hereda, igual que hereda la identidad y el directorio de trabajo. Por eso la salida de ls aparece en la misma ventana en que escribiste.

El sistema ofrece además tres nombres fijos para referirse a los canales del proceso que los usa, útiles cuando un programa exige un nombre de archivo:

terminal
$ ls -l /dev/stdin /dev/stdout /dev/stderr
lrwxrwxrwx 1 root root 15 oct 12 09:02 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 oct 12 09:02 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 oct 12 09:02 /dev/stdout -> /proc/self/fd/1

Son enlaces simbólicos, de los que estudiamos en el tema 14, hacia los descriptores 0, 1 y 2.

5. La salida y los errores son dos canales

En la pantalla, los resultados y los errores de un comando aparecen juntos, y nada indica cuál es cuál. Para comprobar que viajan por canales distintos hay que separarlos. Lo hacemos con un comando que produce las dos cosas a la vez: listar un archivo que existe y otro que no.

terminal, en ~/practica
$ cd ~/practica
$ ls notas.txt nada.txt
ls: cannot access 'nada.txt': No such file or directory
notas.txt

Dos líneas, mezcladas en la pantalla. Ahora descartamos uno de los canales por vez, enviándolo a /dev/null, el sumidero del tema 8:

terminal
$ ls notas.txt nada.txt > /dev/null             # descartar el canal 1
ls: cannot access 'nada.txt': No such file or directory
$ ls notas.txt nada.txt 2> /dev/null            # descartar el canal 2
notas.txt
$ ls notas.txt nada.txt > /dev/null 2> /dev/null   # descartar los dos
$ echo $?
2
Se descartóQuedó en la pantallaEntonces…
El canal 1El mensaje de error.El mensaje no viajaba por el canal 1.
El canal 2El nombre del archivo.El resultado no viajaba por el canal 2.
Los dosNada.No hay un tercer canal de salida.

La demostración es completa. El signo > solo, sin número, se refiere al canal 1; es una abreviatura de 1>. Los operadores los estudiamos en detalle en el tema siguiente.

El último comando deja ver algo más: con las dos salidas silenciadas, el fallo sigue siendo detectable por el código de salida, que vale 2. Volvemos sobre eso en la sección 14.

6. Por qué los errores van aparte

Tener dos canales de salida parece una complicación. Se entiende con un caso en que la salida no va a la pantalla:

terminal
$ ls notas.txt nada.txt | wc -l
ls: cannot access 'nada.txt': No such file or directory
1

La salida de ls se envió a wc -l, para contar las líneas. El resultado es 1: hay un archivo. El mensaje de error no fue contado, y además llegó a la pantalla, donde alguien puede leerlo. Si los errores viajaran por el mismo canal que los resultados, habrían ocurrido dos cosas malas:

Con un solo canal…Consecuencia
El mensaje de error entraría a wc, mezclado con los datos.El resultado sería 2, un número equivocado, sin ninguna señal de que algo anduvo mal.
El mensaje no aparecería en la pantalla.Nadie se enteraría del error.

Esa es la razón del diseño, y la dio la experiencia: en las primeras versiones de Unix había un solo canal de salida, y los mensajes de error terminaban dentro de los archivos y de los documentos que se enviaban a imprimir. El segundo canal se agregó para corregirlo.

El principio es que los datos y los comentarios sobre los datos no deben mezclarse. El canal 1 lleva información para ser procesada por quien siga; el canal 2, información para la persona que opera.

7. Qué se escribe en cada canal

La separación no es solo entre «lo que salió bien» y «lo que salió mal». Por el canal de errores viaja todo lo que no es el resultado propiamente dicho:

Va a la salida estándar (1)Va al error estándar (2)
El resultado del comando: el listado, el texto filtrado, el número contado.Los mensajes de error.
Lo que tiene sentido guardar en un archivo o pasar a otro programa.Los avisos, aunque no impidan continuar.
El texto de --help, cuando se lo pide.Los indicadores de progreso y las barras de avance.
Las preguntas al usuario, como las de rm -i.
El mensaje de uso, cuando el comando se invocó mal.

Varios casos de temas anteriores encajan aquí. Los Permission denied de find, que no eran fallas de la búsqueda: iban por el canal 2, y por eso 2>/dev/null los quitaba sin afectar los resultados. El aviso Removing leading '/' de tar. La pregunta overwrite? de cp -i, que tiene que llegar a la pantalla aunque la salida esté redirigida.

La diferencia entre las dos últimas filas de cada columna es instructiva. El mismo texto de ayuda va al canal 1 si lo pediste con --help, porque entonces es el resultado; y al canal 2 si apareció porque te equivocaste, porque entonces es un error.

Un programa bien escrito respeta esta disciplina, y vale la pena recordarla para cuando escribas los tuyos: es lo que hace que se puedan combinar con otros.

8. La entrada estándar

El canal 0 es el que más se pasa por alto, porque en el uso habitual no se nota. Casi todos los comandos de texto pueden recibir sus datos de dos maneras:

Se invocaLee los datos de
wc -l notas.txtEl archivo indicado.
wc -lLa entrada estándar.

Sin un archivo como argumento, el programa lee del canal 0, que por defecto es el teclado. Lo encontramos de paso en el tema 3, con cat. Se ve mejor con un comando que procesa lo que recibe:

terminal: sort lee del teclado
$ sort
pera
manzana
uva
[Ctrl+D]
manzana
pera
uva

Tras ejecutar sort no aparece el indicador: el programa está esperando datos. Cada línea que se escribe le llega al pulsar Enter. Cuando se le indica que no hay más, con Ctrl+D, ordena lo recibido y lo escribe en su salida. Luego termina, y vuelve el indicador.

Nadie usa sort así, tecleando los datos. El ejemplo importa por lo que revela: sort no tiene un «modo teclado» y un «modo archivo». Tiene una entrada. Lo que hoy es el teclado, mañana será un archivo o la salida de otro comando, y sort hará lo mismo sin enterarse del cambio.

9. El fin de la entrada: Ctrl+D

Un programa que lee de un archivo sabe cuándo terminar: cuando el archivo se acaba. Con el teclado no hay un final natural, y hace falta una manera de señalarlo.

El fin de archivo, o EOF (end of file), es la condición que indica a un programa que no hay más datos que leer en un canal. Desde el teclado se produce pulsando Ctrl+D al comienzo de una línea.

Ctrl+D no envía un carácter al programa: le dice a la terminal «entrega lo que haya y, si no hay nada, avisa que la entrada terminó». El programa recibe ese aviso, completa su trabajo y termina de manera normal.

Eso lo distingue de la otra combinación que conocemos, y la diferencia es importante:

Ctrl+DCtrl+C
Significa«No hay más datos.»«Detente ahora.»
El programaTermina su trabajo con lo recibido y sale normalmente.Es interrumpido, sin terminar.
El resultadoSe produce.Se pierde.
Código de salida0, si todo fue bien.130.

En el ejemplo de sort, Ctrl+D produce la lista ordenada; Ctrl+C la habría descartado. Con wc -l, Ctrl+D da la cuenta de líneas, y Ctrl+C, nada.

Y aquí se explica algo del tema 2, donde dijimos que Ctrl+D cierra la terminal. No hay una regla especial: la shell es un programa que lee comandos de su entrada estándar. Al recibir el fin de archivo entiende que no llegarán más, y termina, como cualquier otro.

10. Cuando un comando parece colgado

La consecuencia práctica más frecuente de lo anterior es un susto. Se escribe un comando, se pulsa Enter, y no pasa nada: ni resultado, ni error, ni indicador. La causa suele ser que se olvidó el nombre del archivo, y el comando está leyendo del teclado.

terminal: el archivo se olvidó
$ grep pan                          # faltó el archivo: grep espera datos del teclado
▌

No está colgado ni consume recursos: espera, con toda paciencia, a que alguien escriba. Es el mismo estado que el del indicador > del tema 5, pero sin señal alguna. Reconocerlo ahorra muchos minutos:

SíntomaCausa probableSalida
El cursor queda en una línea vacía, sin indicador.El comando lee de la entrada estándar.Ctrl+C para cancelar, o Ctrl+D para decirle que no hay datos.
Aparece un indicador >.La shell espera que se complete la línea: falta una comilla.Ctrl+C.
El cursor no responde a ninguna tecla.Se pulsó Ctrl+S.Ctrl+Q.

Los comandos que caen en esta situación son los que procesan texto: cat, grep, sort, wc, head, tail, sed, awk. Todos esperan un archivo o, en su defecto, la entrada.

11. Filtros, fuentes y sumideros

No todos los comandos usan los tres canales. Según cuáles usen, cumplen papeles distintos, y esa clasificación es la que permite saber de antemano cómo se pueden combinar:

PapelLee de la entradaEscribe en la salidaEjemplos
Fuente: produce datosNoSíls, echo, date, find, ps, history, cat archivo
Filtro: transforma datosSíSísort, grep, head, tail, wc, cut, tr, sed, awk
Sumidero: consume datosSíNo, o no para seguirless, que los muestra; una impresora; un programa que envía un correo
Ninguno: actúa sobre archivosNoNocp, mv, rm, mkdir, chmod, cd

Un filtro es un programa que lee texto de su entrada estándar, lo transforma y escribe el resultado en su salida estándar.

Los filtros son los protagonistas de este bloque y del siguiente. Cada uno hace una transformación sencilla —ordenar, seleccionar, contar, recortar— y se encadenan porque la salida de uno tiene la forma que espera la entrada del otro: líneas de texto.

La última fila es una advertencia útil. A un comando que no lee su entrada no sirve de nada enviársela:

terminal: un intento que no hace lo que parece
$ echo "notas.txt" | ls -l         # ls no lee su entrada: lista el directorio, como siempre
total 28
drwxrwxr-x 2 ana ana  4096 oct 12 10:40 fotos
…

ls recibe sus nombres como argumentos, no por la entrada, e ignoró lo que le llegaba. Convertir líneas de entrada en argumentos es tarea de otro comando, xargs, que vemos en el tema 24.

12. Leer un archivo o leer la entrada

Para un filtro, las dos formas de la sección 8 dan el mismo resultado, con una diferencia pequeña que confirma todo el modelo:

terminal
$ wc -l notas.txt                   # el archivo, como argumento
1 notas.txt
$ wc -l < notas.txt                 # el archivo, conectado a la entrada
1
wc -l notas.txtwc -l < notas.txt
Quién abre el archivowcLa shell
Qué recibe wcUn argumento: el nombreNingún argumento; datos por el canal 0
Sabe wc el nombreSí, y lo muestraNo

En el segundo caso, wc no puede mostrar el nombre porque nunca lo supo. Recibió un canal del que leer, ya conectado, y eso es todo. Lo notamos en el tema 12 sin poder explicarlo.

Hay comandos que solo leen su entrada y no aceptan archivos. Y hay una convención para pedir la entrada de manera explícita donde se espera un nombre de archivo: el guion solo, -, que presentamos en el tema 3. cat encabezado.txt - pie.txt escribe el primer archivo, luego lo que llegue por la entrada, luego el segundo.

13. Quién conecta los canales

Queda decir quién decide adónde va cada canal. La respuesta ya apareció dos veces en el tema: la shell, antes de ejecutar el comando.

Es el mismo esquema que vimos con la expansión, en el tema 9. Allí la shell reescribía la línea; aquí prepara las conexiones. En los dos casos, cuando el programa arranca el trabajo ya está hecho, y el programa no sabe que ocurrió.

  1. Lee la línea y reconoce los operadores: >, <, |. shell
  2. Abre los archivos indicados, o crea las tuberías. shell
  3. Crea el proceso y coloca en sus descriptores 0, 1 y 2 lo que corresponde. shell
  4. Quita los operadores de la línea: no se pasan como argumentos. shell
  5. El programa se ejecuta, lee del 0 y escribe en el 1 y en el 2, como siempre. comando

De este orden salen tres consecuencias que ya encontramos, cada una en su momento, como rarezas:

HechoExplicaciónVisto en
sudo echo x > /etc/archivo falla.El archivo lo abre la shell, con tu identidad, en el paso 2. sudo todavía no se ejecutó.Tema 19
Si el archivo de salida no se puede crear, el comando no se ejecuta.La falla ocurre en el paso 2, antes del paso 5.—
ls cambia de formato cuando su salida no es la pantalla.El programa puede preguntar si su canal 1 es una terminal, y adaptarse.Tema 7

La tercera merece una aclaración, porque matiza lo dicho en la sección 1. Un programa no sabe adónde va su salida, pero sí puede averiguar si es una terminal. Unos cuantos lo aprovechan para ser más amables con las personas: columnas y colores en ls, colores en grep, barras de progreso. Cuando la salida va a un archivo o a otro programa, escriben texto simple, que es lo que conviene procesar.

14. Las cuatro vías de un comando

Con este tema se completa el cuadro de cómo se comunica un comando con el exterior. Son cuatro vías, y cada una tiene su propósito:

VíaDirecciónLlevaVisto en
ArgumentosHacia el comandoQué hacer y sobre qué: opciones y nombres.Tema 3
Entrada estándarHacia el comandoLos datos a procesar.Este
Salida y error estándarDesde el comandoLos resultados, y los mensajes.Este
Código de salidaDesde el comandoUn número: si tuvo éxito.Tema 3

El código de salida funciona como un cuarto canal, mínimo, que no lleva texto sino un veredicto. Complementa a los otros: un programa escribe en el canal 2 qué salió mal, para una persona, y devuelve un número distinto de cero para que otro programa sepa que algo salió mal. En la sección 5 lo vimos sobrevivir al silenciamiento de las dos salidas. Sobre él se construye el encadenamiento de comandos del tema 25.

El bloque desarrolla cada conexión posible de estos canales:

TemaConecta los canales con…Operadores
22, esteLa terminal: el caso por defecto.—
23Archivos.>, >>, <, 2>, 2>&1
24Otros comandos.|
25Los resultados de un comando con la ejecución de otro.;, &&, ||, $( )

Y la referencia del tema:

ConceptoDetalle
Entrada estándar, stdinDescriptor 0. Por defecto, el teclado.
Salida estándar, stdoutDescriptor 1. Por defecto, la pantalla.
Error estándar, stderrDescriptor 2. Por defecto, la pantalla.
Ctrl+DFin de la entrada. El programa termina normalmente.
Ctrl+CInterrupción. El programa no termina su trabajo.
ls -l /proc/self/fdMuestra a qué está conectado cada descriptor.
ttyMuestra el archivo de dispositivo de la terminal.

En el tema 23 usamos el modelo: conectamos cada uno de los tres canales a un archivo, por separado y juntos, y vemos todas las formas de hacerlo.

15. Resumen

16. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Cuáles son los tres canales estándar y qué número tiene cada uno?

La entrada estándar, 0; la salida estándar, 1; y el error estándar, 2.

2. ¿A qué están conectados por defecto?

Los tres a la terminal: la entrada recibe lo que se escribe en el teclado, y las dos salidas aparecen en la pantalla.

3. ¿Qué es un descriptor de archivo?

Un número con el que un proceso se refiere a un archivo, o a otro objeto de lectura o escritura, que tiene abierto.

4. ¿Qué significa el 2 en 2>/dev/null?

Es el descriptor del error estándar. La expresión envía ese canal al sumidero, con lo que los mensajes de error se descartan.

5. En la pantalla, la salida y los errores se ven juntos. ¿Cómo se comprueba que son canales distintos?

Redirigiendo uno por vez. Con > /dev/null desaparecen los resultados y quedan los errores; con 2> /dev/null, al revés.

6. ¿Por qué los mensajes de error no viajan por la salida estándar?

Porque se mezclarían con los resultados y serían procesados como datos por el comando siguiente, o quedarían dentro de un archivo, sin que la persona que opera los viera.

7. Además de los errores, ¿qué otras cosas se escriben en el canal 2?

Los avisos, los indicadores de progreso, las preguntas al usuario y el mensaje de uso cuando el comando se invocó mal.

8. ¿De dónde lee sort si se lo ejecuta sin argumentos?

De la entrada estándar, que por defecto es el teclado. Espera líneas hasta recibir el fin de entrada.

9. ¿Qué diferencia hay entre Ctrl+D y Ctrl+C?

Ctrl+D indica que no hay más datos: el programa procesa lo recibido y termina normalmente. Ctrl+C lo interrumpe, y el resultado se pierde.

10. ¿Por qué Ctrl+D cierra la shell?

Porque la shell lee los comandos de su entrada estándar. Al recibir el fin de archivo, entiende que no habrá más comandos y termina.

11. Escribiste grep error y pulsaste Enter. No aparece nada. ¿Qué ocurre?

Falta el nombre del archivo, y grep está leyendo de la entrada estándar: espera que escribas. Se sale con Ctrl+C.

12. ¿Qué es un filtro? Da tres ejemplos.

Un programa que lee texto de su entrada estándar, lo transforma y escribe el resultado en su salida estándar. Por ejemplo, sort, grep y wc.

13. ¿Por qué echo archivo.txt | rm no borra el archivo?

Porque rm no lee su entrada estándar: recibe los nombres como argumentos.

14. ¿Por qué wc -l < notas.txt no muestra el nombre del archivo?

Porque el archivo lo abre la shell y lo conecta a la entrada del comando. wc no recibe ningún nombre: solo datos.

15. ¿Cuáles son las cuatro vías por las que un comando se comunica con el exterior?

Los argumentos y la entrada estándar, por las que recibe; las salidas estándar y de error, y el código de salida, por las que entrega.

Para practicar

  1. Ejecuta tty y luego ls -l /proc/self/fd. Abre otra terminal y repite. ¿Qué cambia?
  2. Ejecuta ls -l /proc/self/fd > /tmp/fd.txt y mira el archivo con cat. ¿A qué apuntaba el descriptor 1 durante ese comando?
  3. Repite la demostración de la sección 5 con cat notas.txt nada.txt: descarta cada canal por separado, y luego los dos, y mira el código de salida.
  4. Ejecuta find /etc -name "*.conf" | wc -l. ¿Los mensajes de permiso denegado fueron contados? ¿Por qué aparecieron en la pantalla?
  5. Ejecuta cat sin argumentos, escribe tres líneas y termina con Ctrl+D. Repite terminando con Ctrl+C, y compara los códigos de salida con echo $?.
  6. Ejecuta wc sin argumentos, escribe un párrafo y pulsa Ctrl+D. Repite con sort -r y con head -n 2. ¿En qué momento escribe cada uno su resultado?
  7. Clasifica estos comandos como fuente, filtro o ninguno: date, tail, mkdir, whoami, tr, pwd, uniq, touch.
  8. Compara wc -c informe.txt, wc -c < informe.txt y cat informe.txt | wc -c. ¿En cuáles conoce wc el nombre del archivo?
  9. Ejecuta ls --color=auto y ls --color=auto | cat. Explica la diferencia con lo dicho en la sección 13.
  10. Prueba ls --help | head -n 3 y ls --opcion-falsa | head -n 3. ¿En cuál actuó head, y qué dice eso sobre el canal por el que viajó cada texto?