Programación concurrente en Linux

# Entrada/Salida Estándar

Las ideas de filtro y redirección son de una sencillez conceptual desconcertante. Puedes imaginarte el filtro como un elemento con dos mangueras flexibles. Por una entra un flujo de información (stream en la jerga de UNIX) y por la otra sale otro flujo convenientemente procesado. Las mangueras puedes conectarlas a ficheros (es decir, dispositivos, ficheros ordinarios y otros elementos que ya descubriremos). Siguiendo con la metáfora, si el filtro es una depuradora de agua, por la manguera de entrada entra agua sucia y por la de salida sale agua potable. Puedes imaginar una salida adicional por la que, en caso de mal funcionamiento, saldría aceite, humo o cualquier otro subproducto no relacionado con la función de la depuradora.

¿Cómo se construye un programa que se comporte de acuerdo a ese modelo? En realidad, los primeros programas que construimos utilizando las funciones de la biblioteca estándar de C más sencillas (`getchar`, `scanf`, `putchar`, `printf`) funcionan según este modelo. Estas funciones son así de simples porque no hay que especificar de dónde viene la entrada o a dónde va la salida. En estas funciones se asume que la entrada/salida tiene un origen/destino predeterminado, lo que se denomina entrada/salida estándar.

Los usuarios noveles de Linux piensan que la entrada estándar es el teclado y la salida estándar la pantalla. Esta idea no es correcta, como ya has podido comprobar. En efecto, cuando redireccionas un filtro para que tome la entrada de otro dispositivo (más formalmente, fichero), el filtro leerá ahora de ese dispositivo y no del teclado ...¡y su código seguirá siendo el mismo! El dispositivo teclado/pantalla, o terminal, de nombre `/dev/tty`, suele ser la asignación inicial de la entrada/salida estándar. Esto es lo que observas cuando abres la aplicación terminal en Linux. Pero precisamente, el hecho de ser entrada/salida estándar significa que esa asignación se puede cambiar mediante la redirección. Y esto es aplicable al propio shell, que no es sino un filtro. Ya sabíamos que podemos ejecutar el `bash` como cualquier otro programa. Pues bien, ahora ya sabes que puedes hacer:

    bash < entrada > salida

donde `entrada` es un fichero de texto donde previamente has metido órdenes. Y no es necesario que sea ejecutable.

Si buscas en el `man` las funciones de entrada/salida estándar encontrarás que estas tienen asociadas respectivas compañeras (por ejempo, `getc` para `getchar`) caracterizadas por un argumento extra. Como verás, ese argumento no indica un nombre de fichero o dispositivo, sino lo que se conoce como descriptor de fichero, file descriptor. Un descriptor de fichero es un nombre lógico con el que el programa se refiere a un fichero. En algún momento, el sistema operativo o un programa, tal vez el shell, asocian al descriptor de fichero un fichero (o dispositivo, ya que siempre me estoy refiriendo a la abstracción). Dejaremos para más adelante cómo se hace esa asociación. De momento sabemos que podemos describir la entrada/salida estándar en términos de descriptores de fichero.

Una operación de entrada/salida estándar no requiere especificar un descriptor de fichero, por ejemplo `getchar()`. Pero, ¿existen descriptores de ficheros explícitos, por ejemplo para usar en funciones de biblioteca de C como `getc()`, para la entrada/salida estándar? La respuesta es afirmativa:

- `stdin` es el descriptor de fichero para la entrada estándar.

- `stdout` es el descriptor de fichero para la salida estándar.

- `stderr` es el descriptor de fichero para la salida estándar de error.