¿Cómo puedo canalizar stderr y no stdout?

Resuelto asked hace 14 años • 11 respuestas

Tengo un programa que escribe información en stdoutand stderr, y necesito procesar stderrwith grep, dejando stdoutde lado.

Usando un archivo temporal, se podría hacerlo en dos pasos:

command > /dev/null 2> temp.file
grep 'something' temp.file

Pero, ¿cómo se puede lograr esto sin archivos temporales, usando un comando y tuberías?

 avatar Feb 26 '10 22:02
Aceptado

Primero redirija stderr a stdout: la tubería; luego redirija stdout a /dev/null(sin cambiar hacia dónde va stderr):

command 2>&1 >/dev/null | grep 'something'

Para obtener detalles sobre la redirección de E/S en toda su variedad, consulte el capítulo sobre Redirecciones en el manual de referencia de Bash.

Tenga en cuenta que la secuencia de redirecciones de E/S se interpreta de izquierda a derecha, pero las canalizaciones se configuran antes de que se interpreten las redirecciones de E/S. Los descriptores de archivos como 1 y 2 son referencias para abrir descripciones de archivos. La operación 2>&1hace que el descriptor de archivo 2, también conocido como stderr, se refiera a la misma descripción de archivo abierto a la que se refiere actualmente el descriptor de archivo 1, también conocido como stdout (ver dup2()y open()). Luego , la operación >/dev/nullcambia el descriptor de archivo 1 para que se refiera a una descripción de archivo abierto para /dev/null, pero eso no cambia el hecho de que el descriptor de archivo 2 se refiere a la descripción de archivo abierto a la que apuntaba originalmente el descriptor de archivo 1, es decir, la tubería.

Jonathan Leffler avatar Feb 26 '2010 15:02 Jonathan Leffler

O para intercambiar la salida de error estándar y salida estándar, use:

command 3>&1 1>&2 2>&3

Esto crea un nuevo descriptor de archivo (3) y lo asigna al mismo lugar que 1 (salida estándar), luego asigna fd 1 (salida estándar) al mismo lugar que fd 2 (error estándar) y finalmente asigna fd 2 (error estándar ) al mismo lugar que fd 3 (salida estándar).

El error estándar ahora está disponible como salida estándar y la salida estándar anterior se conserva en error estándar. Esto puede ser excesivo, pero es de esperar que brinde más detalles sobre los descriptores de archivos Bash (hay nueve disponibles para cada proceso).

Kramish avatar Mar 04 '2010 18:03 Kramish

En Bash, también puedes redirigir a un subshell usando la sustitución de procesos :

command > >(stdout pipe)  2> >(stderr pipe)

Para el caso que nos ocupa:

command 2> >(grep 'something') >/dev/null
Rich Johnson avatar Feb 09 '2012 19:02 Rich Johnson

Combinando lo mejor de estas respuestas, si lo hace:

command 2> >(grep -v something 1>&2)

...entonces todas las salidas estándar se conservan como salidas estándar y todos los stderr se conservan como stderr, pero no verá ninguna línea en stderr que contenga la cadena "algo".

Esto tiene la ventaja única de no revertir ni descartar stdout y stderr, ni juntarlos, ni utilizar archivos temporales.

Pinko avatar Apr 10 '2013 21:04 Pinko

Es mucho más fácil visualizar las cosas si piensas en lo que realmente sucede con las "redirecciones" y las "canalizaciones". Los redireccionamientos y canalizaciones en bash hacen una cosa: modificar dónde apuntan los descriptores de archivos de proceso 0, 1 y 2 (consulte /proc/[pid]/fd/*).

Cuando una tubería o "|" El operador está presente en la línea de comando, lo primero que sucede es que bash crea un quince y apunta el FD 1 del comando del lado izquierdo a este quince, y apunta el FD 0 del comando del lado derecho al mismo quince.

A continuación, los operadores de redireccionamiento para cada lado se evalúan de izquierda a derecha y la configuración actual se utiliza siempre que se produce una duplicación del descriptor. Esto es importante porque, dado que la tubería se instaló primero, FD1 (lado izquierdo) y FD0 (lado derecho) ya han cambiado de lo que normalmente habrían sido, y cualquier duplicación de estos reflejará ese hecho.

Por lo tanto, cuando escribe algo como lo siguiente:

command 2>&1 >/dev/null | grep 'something'

Esto es lo que sucede, en orden:

  1. Se crea una tubería (fifo). El "comando FD1" apunta a esta tubería. "grep FD0" también apunta a esta tubería
  2. El "comando FD2" apunta a donde apunta actualmente el "comando FD1" (la tubería)
  3. "comando FD1" apunta a /dev/null

Entonces, toda la salida que el "comando" escribe en su FD 2 (stderr) llega a la tubería y es leída por "grep" en el otro lado. Toda la salida que el "comando" escribe en su FD 1 (stdout) llega a /dev/null.

Si en cambio ejecutas lo siguiente:

command >/dev/null 2>&1 | grep 'something'

Esto es lo que sucede:

  1. Se crea una tubería y el "comando FD 1" y "grep FD 0" apuntan a ella.
  2. "comando FD 1" apunta a /dev/null
  3. "comando FD 2" apunta a donde apunta actualmente FD 1 (/dev/null)

Entonces, todas las salidas estándar y stderr del "comando" van a /dev/null. Nada llega a la tubería y, por lo tanto, "grep" se cerrará sin mostrar nada en la pantalla.

También tenga en cuenta que las redirecciones (descriptores de archivos) pueden ser de solo lectura (<), de solo escritura (>) o de lectura y escritura (<>).

Una nota final. Si un programa escribe algo en FD1 o FD2, depende totalmente del programador. Las buenas prácticas de programación dictan que los mensajes de error deben ir a FD 2 y la salida normal a FD 1, pero a menudo encontrará programación descuidada que mezcla los dos o ignora la convención.

Michael Martinez avatar Aug 20 '2013 18:08 Michael Martinez