stderr, stdout y más

En uno de los posts anteriores explique un poco el tema del stdin y stdout. Sabemos que podemos encauzar la salida (stdout) de un programa como entrada para otro (stdin) y de esta manera realizar tareas complejas utilizando herramientas sencillas.
A parte de estos dos descriptores, tenemos el stderr o salida de errores. Básicamente, stderr es lo mismo que stdout pero contiene posibles errores que el programa quiera reportar por pantalla al usuario.
Por ejemplo, vamos a utilizar un error producido por "cat" y lo vamos a almacenar en un fichero utilizando ">"
$cat foo.txt > foo.log

En este caso, foo.txt no existe. Dado que ">" es utilizado sólo para stdout, el fichero foo.log será creado, pero está vacío. En cambio, podremos ver en la consola el error devuelto por stderr:
cat: foo.txt: No such file or directory
Si queremos capturar este mensaje de error en un fichero foo.err tendremos que añadir el número "2" a ">":
cat foo.txt 2> foo.log

Aquí le estamos indicando a Bash que capture el descriptor número 2 (stderr) y lo envié al fichero. Recordemos que stdin es 0 y stdout es 1.
De igual manera podemos mezclar stdout y stderr. La utilidad de esto la encontramos cuando queremos ejecutar algún programa de forma automática (utilizando cron, por ejemplo) o desde un script y deseamos capturar toda la información posible en algún fichero al que podamos acceder posteriormente.
Entonces, el tema es bastante sencillo. Vamos a hacer un "tail" de dos ficheros y todas las salidas vamos a enviarlas a dos ficheros foo.out y foo.err.
$tail /var/log/dmesg foo.txt >foo.out 2>foo.err

dmesg sabemos que existe, por lo tanto las ultimas lineas devueltas por "tail" serán capturadas por el stdout al fichero foo.out. En cambio el error devuelto al no encontrar el fichero foo.txt será enviado por stderr a foo.err:
tail: cannot open foo.txt for reading: No such file or directory
También podemos enviar las dos salidas a un único fichero e incluso encauzarlas como stdin para otro programa. En el primer caso vamos a utilizar "2>&1" lo cual vamos a ver en muchos scripts.
$tail /var/log/dmesg foo.txt >foo.out 2>&1

La mejor manera de silenciar a un programa para que no perturbe la ejecución de nuestro script es enviar todas las salidas a /dev/null.
$tail /var/log/dmesg foo.txt >/dev/null 2>&1

Si nuestro script necesita ejecutar otro programa y necesita sólo de su "exit status" el usuario que ejecuta el script no verá cuales programas estamos utilizando y no recibirá los posibles errores que de otra manera aparecerían en pantalla.
En segundo lugar, podemos realizar otro tipo de comprobaciones dirigiendo stderr y stdout como stdin de otro programa. Supongamos que creamos un script que sólo funciona con OpenSSH. Para comprobar que la versión que está instalada en el sistema es OpenSSH podemos utilizar lo siguiente:
$/usr/bin/ssh -v 2>&1 | grep -q OpenSSH ; echo $?
0

Si nuestro cliente de SSH no fuera el OpenSSH obtendriamos lo siguiente:
$/usr/bin/ssh -v 2>&1 | grep -q OpenSSH ; echo $?
1
En el caso de que nuestra versión de "grep" no tenga la opción "q quiet" volvemos a utilizar /dev/null
ssh -v 2>&1 | grep OpenSSH >/dev/null 2>&1 ; echo $?
0

Donde $? es una variable que contiene el "exit status" del último comando ejecutado, en este ejemplo es grep.