¿Cómo pipetear stderr, y no stdout?

Tengo un progtwig que escribe información para stdout y stderr , y necesito grep lo que viene a stderr , sin tener en cuenta el stdout .

Por supuesto puedo hacerlo en 2 pasos:

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

pero preferiría poder hacer esto sin archivos temporales. ¿Hay algún truco de tubería inteligente?

Primero redirige stderr a stdout: la tubería; luego, redirija la salida estándar a /dev/null (sin cambiar el destino de stderr):

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

Para obtener más información 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. Las descripciones de archivos como 1 y 2 son referencias para abrir descripciones de archivos. La operación 2>&1 hace que el descriptor de archivo 2 aka stderr se refiera a la misma descripción de archivo abierto que el descriptor de archivo 1 también conocido como stdout (consulte dup2() y open() ). La operación >/dev/null cambia el descriptor de archivo 1 para que haga referencia a una descripción de archivo abierta 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, que era el descriptor de archivo 1 originalmente apuntando a: a saber, la tubería.

O para intercambiar la salida de stderr y stdout sobre el uso: –

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

Esto crea un nuevo descriptor de archivo (3) y lo asigna al mismo lugar que 1 (stdout), luego asigna fd 1 (stdout) al mismo lugar que fd 2 (stderr) y finalmente asigna fd 2 (stderr) al mismo colocar como fd 3 (stdout). Stderr ahora está disponible como stdout y stdout antiguo conservado en stderr. Esto puede ser excesivo, pero con suerte brinda más detalles sobre los descriptores de archivos bash (hay 9 disponibles para cada proceso).

En Bash, también puede redirigir a una subcadena utilizando la sustitución de proceso :

 command > >(stdlog pipe) 2> >(stderr pipe) 

Para el caso que nos ocupa:

 command 2> >(grep 'something') >/dev/null 

Combinando la mejor de estas respuestas, si lo haces:

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

… luego todo stdout se conserva como stdout y stderr se conserva como stderr, pero no verá ninguna línea en stderr que contenga la cadena “algo”.

Esto tiene la ventaja única de no invertir ni descartar stdout y stderr, ni arrastrarlos juntos, ni usar ningún archivo temporal.

Es mucho más fácil visualizar cosas si piensas en lo que realmente está pasando con “redirecciones” y “tuberías”. Los redireccionamientos y las canalizaciones en bash hacen una cosa: modificar dónde apuntan los descriptores de archivo de proceso 0, 1 y 2 (ver / proc / [pid] / fd / *).

Cuando una pipa o “|” el operador está presente en la línea de comando, lo primero que ocurre es que bash crea un fifo y apunta el FD 1 del comando del lado izquierdo a este fifo, y apunta el FD 0 del comando del lado derecho al mismo fifo.

A continuación, los operadores de redirección para cada lado se evalúan de izquierda a derecha , y la configuración actual se usa siempre que se produce una duplicación del descriptor. Esto es importante porque dado que el tubo se configuró primero, el FD1 (lado izquierdo) y el FD0 (lado derecho) ya han cambiado de lo que normalmente podrían haber 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 hacia donde actualmente apunta el “comando FD1” (el conducto)
  3. “comando FD1” apunta a / dev / null

Por lo tanto, toda la salida que el “comando” escribe en su FD 2 (stderr) hace su camino hacia la tubería y es leída por “grep” en el otro lado. Toda la salida que “comando” escribe en su FD 1 (stdout) hace su camino a / dev / null.

Si, en cambio, ejecuta lo siguiente:

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

Esto es lo que sucede:

  1. se crea una tubería y se señalan “comando FD 1” y “grep FD 0”
  2. “comando FD 1” apunta a / dev / null
  3. El “comando FD 2” apunta hacia donde actualmente FD 1 apunta (/ dev / null)

Entonces, todos stdout y stderr de “comando” van a / dev / null. Nada va a la tubería y, por lo tanto, “grep” se cerrará sin mostrar nada en la pantalla.

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

Una nota final. Si un progtwig escribe algo en FD1 o FD2, depende completamente del progtwigdor. Una buena práctica de progtwigción dicta que los mensajes de error deben ir a FD 2 y la salida normal a FD 1, pero a menudo encontrará una progtwigción descuidada que mezcla los dos o ignora la convención.

¿Estás usando bash? Si es así:

 command >/dev/null |& grep "something" 

http://www.gnu.org/software/bash/manual/bashref.html#Pipelines

Para aquellos que desean redirigir stdout y stderr permanentemente a los archivos, grep on stderr, pero mantengan el stdout para escribir mensajes en un tty:

 # save tty-stdout to fd 3 exec 3>&1 # switch stdout and stderr, grep (-v) stderr for nasty messages and append to files exec 2> >(grep -v "nasty_msg" >> std.err) >> std.out # goes to the std.out echo "my first message" >&1 # goes to the std.err echo "a error message" >&2 # goes nowhere echo "this nasty_msg won't appear anywhere" >&2 # goes to the tty echo "a message on the terminal" >&3 

Esto redirigirá command1 stderr a command2 stdin, mientras deja command1 stdout como está.

 exec 3>&1 command1 2>&1 >&3 3>&- | command2 3>&- exec 3>&- 

Tomado de LDP

Acabo de encontrar una solución para enviar stdout a un comando y stderr a otro, usando pipes con nombre.

Aquí va.

 mkfifo stdout-target mkfifo stderr-target cat < stdout-target | command-for-stdout & cat < stderr-target | command-for-stderr & main-command 1>stdout-target 2>stderr-target 

Probablemente sea una buena idea eliminar las tuberías nombradas después.

Intento seguir, encuentro que también funciona,

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