Cómo saber si la salida de un comando o script de shell es stdout o stderr

32

Digamos que ejecuto un comando o script de shell, y me da salida. Sin conocer los aspectos internos de este comando o script de shell, ¿cómo se determina si el resultado proviene de stderro stdout?

Por ejemplo,

$ ls -ld /
drwxrwxr-t  35 root  admin  1258 Dec 11 19:16 /

vs

ls -ld /test
ls: /test: No such file or directory

¿Cómo puedo determinar si el primer comando se imprimió stdouty el segundo en stderr(¿lo hizo?)

KM.
fuente
1
¿Qué problema estás tratando de resolver aquí?
Kenster
3
Temía que alguien preguntara eso; ninguno realmente, sobre todo curioso y con la esperanza de mejorar mi comprensión de la redirección.
KM.
77
Puede poner stderreden su entorno de shell LD_PRELOADpara obtener stdouty stderren diferentes colores. Aquí hay una pregunta relacionada en ese sentido.
Anko

Respuestas:

18

No hay forma de saber una vez que la salida ya se ha impreso. En este caso, tanto stdouty stderrestán conectados al terminal, por lo que la información sobre la cual corriente fue escrito para que ya se perdió en el momento en el texto apareció en su terminal; fueron combinados por el programa antes de llegar a la terminal.

Lo que puede hacer, en un caso como el anterior, sería ejecutar el comando stdouty stderrredirigirse a diferentes lugares y ver qué sucede. O ejecútelo dos veces, una vez con stdoutredireccionado a /dev/nully una vez con stderrredireccionado a /dev/null, y vea cuál de esos casos da como resultado que aparezca el texto.

Puede redirigir stdouta /dev/nullvirando >/dev/nullen el extremo de la línea de comandos, y se puede redirigir stderra /dev/nullañadiendo 2>/dev/null.

Godlygeek
fuente
9

Puede redirigir stdout usando > filey redirigir stderr usando 2> file. Muchos shells modernos admiten la redirección a comandos, por lo que puede usar sedpara resaltar qué salida proviene de qué flujo:

$ ls 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
1: unity_support_test.0
1: vmwareDnD

$ ls foo 2> >(sed 's/^/2: /') > >(sed 's/^/1: /')
2: ls: cannot access foo: No such file or directory
Mark Plotnick
fuente
¡Muy agradable! Sería genial si uno pudiera resaltar las líneas en colores en lugar de prefijo.
dotancohen
3
@dotancohen: ¡Uno puede! Por ejemplo,(echo "this is stdout"; echo "this is stderr" >&2) > >(sed 's/.*/\x1b[32m&\x1b[0m/') 2> >(sed 's/.*/\x1b[31m&\x1b[0m/')
PM 2Ring
Tenga en cuenta que si hay una salida mixta de stdout y stderr, primero se imprimirá stdout, luego stderr.
nyuszika7h
5

El annotate-outputscript de Debian's le devscriptspermite hacer esto selectivamente:

$ annotate-output ls -ld /test
14:54:22 -: Started ls -ld /test
14:54:22 E: ls: cannot access /test: No such file or directory
14:54:22 -: Finished with exitcode 2

La segunda columna indica stdout y stderr con Oy Erespectivamente.

Hay algunas advertencias, la principal es como se señaló en las otras respuestas: no puede hacer esto después del hecho. Ni el shell ni el terminal saben cómo un programa arbitrario utiliza sus descriptores de archivo, aunque el shell es responsable de configurarlos inicialmente.

Este método utiliza quince, escribir en un quince puede comportarse de manera diferente que escribir en un tty, y escribir en dos quince diferentes es definitivamente diferente (problemas potenciales de sincronización / intercalación). Además, no es adecuado para uso interactivo, por ejemplo, annotate-output bashno es un gran plan, pero es útil para muchos otros fines. Hay muchos, muchos ejemplos de scripts y funciones de shell en las respuestas a preguntas relacionadas sobre colorear stdin / stdout / stderr, el más robusto es stderrd, que utiliza la modificación en tiempo de ejecución de (la mayoría) de los programas para modificar los datos escritos en stderr.

Esta pregunta a la que se vincula Anko tiene buenas respuestas sobre ese tema relacionado: colorear la salida stdout / stderr: ¿Puedo configurar mi shell para imprimir STDERR y STDOUT en diferentes colores?

Sr. púrpura
fuente
1
Tenga en cuenta que es un bashscript que usa while readbucles y ejecuta un datecomando para cada línea de stdout o stderr, por lo que será mucho menos eficiente que el cmd > >(ts '%T O:') 2> >(ts '%T E:')equivalente.
Stéphane Chazelas
1

Además de las otras respuestas, es interesante señalar /proc/$PID/fd(aunque no responde la pregunta):

$ cat > /dev/null 2> /tmp/blablah &
[1] 3073

[1]+  Stopped                 cat > /dev/null 2> /tmp/blablah
$ ls -l /proc/3073/fd
total 0
lrwx------ 1 kampde kampde 64 Feb 24 11:43 0 -> /dev/pts/33
l-wx------ 1 kampde kampde 64 Feb 24 11:43 1 -> /dev/null
l-wx------ 1 kampde kampde 64 Feb 24 11:43 2 -> /tmp/blablah

Como puede ver, aquí puede ver los descriptores de archivo abiertos para un proceso. 0es el STDIN, 1es el STDOUTy 2es el STDERR. Si no tuviera el STDOUT o el STDERR redirigido, vería /dev/pts/33(al menos en este ejemplo) porque apuntarían al terminal.

notas : /proc/$PIDsolo existe para ejecutar procesos. En este caso lo he usado catsin argumentos para que no termine hasta que cierre el STDIN. También lo ejecuté en segundo plano, así que tengo el PID de inmediato por el bien de este ejemplo.

Carlos Campderrós
fuente
0

No está muy claro lo que está preguntando, pero esto podría ayudar

ls -ld /
echo $?    # Exit status 0 returned because command executed successfully.

ls -ld /test
echo $?    # Non-zero exit status returned -- command failed to execute

Fuente

Si el código de salida es 0, simplemente significa que el comando se ejecuta correctamente (stdout), aquí puede encontrar significados si el código de salida es diferente de 0 (stderr)

empleado
fuente
¡Muy interesante! Nunca hubiera conectado los estados de salida con la redirección. ¿Implica cero stdouty no implica cero stderr?
KM.
77
@KM., No. Diría que se correlaciona fuertemente, pero no hay nada que impida que un programa escriba stderry devuelva un buen código de error, o escriba stdouty devuelva un código de error incorrecto. De hecho, intente find /root, suponiendo que no se esté ejecutando como root, debería imprimir 2 líneas: se imprime "/ root" y se imprime stdout"find: / root: Permiso denegado" stderr. Y find devuelve un código de retorno incorrecto.
godlygeek
El problema de op es claro: cómo saber cuál es stdout o stderr.
it_is_a_literature
0

Por lo general, STDERR tendrá el nombre del programa antepuesto al mensaje con dos puntos.

Ejemplo:

rpm -zq some_utils 
rpm: -zq: unknown option

Vs

rpm -ql some_utils 
package some_utils is not installed
papu
fuente
-1

Para capturar y probar la salida de error:

ls -l test 2>errors
if [ -s errors ]; then echo "There were errors:" && cat errors; fi
rm errors
mlgoth
fuente