Como saber se a saída de um comando ou script de shell é stdout ou stderr

32

Digamos que eu execute um comando ou script de shell, e isso me dá uma saída. Sem conhecer os elementos internos desse comando ou script de shell, como determinar se a saída foi de stderrou stdout?

Por exemplo,

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

vs

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

Como posso verificar se o primeiro comando foi impresso em stdoute o segundo em stderr(foi?)?

KM.
fonte
1
Que problema você está tentando resolver aqui?
Kenster
3
Temia que alguém perguntasse isso; nenhum realmente, principalmente curioso e esperando melhorar meu entendimento de redirecionamento.
KM.
7
Você pode colocar stderredno seu ambiente shell LD_PRELOADpara obter stdoute stderrem cores diferentes. Aqui está uma pergunta relacionada nesse sentido.
Anko

Respostas:

18

Não há como saber quando a saída já foi impressa. Nesse caso, ambos stdoute stderrestão conectados ao terminal, portanto, as informações sobre qual fluxo foi gravado já estavam perdidas quando o texto apareceu no seu terminal; eles foram combinados pelo programa antes mesmo de chegar ao terminal.

O que você pode fazer, em um caso como o acima, seria executar o comando stdoute stderrredirecionar para lugares diferentes e ver o que acontece. Ou execute-o duas vezes, uma vez com stdoutredirecionado para /dev/nulle uma vez com stderrredirecionado para /dev/null, e veja qual desses casos resulta no texto sendo exibido.

Você pode redirecionar stdoutpara /dev/nullalinhavando >/dev/nullno final da linha de comando e stderrpara redirecionar /dev/nulladicionando 2>/dev/null.

godlygeek
fonte
9

Você pode redirecionar o stdout usando > file e redirecionar stderr usando 2> file. Muitos shells modernos oferecem suporte ao redirecionamento para comandos, para que você possa seddestacar qual saída vem de qual fluxo:

$ 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
fonte
Muito agradável! Seria ótimo se pudéssemos destacar as linhas em cores em vez de prefixo.
dotancohen
3
@dotancohen: Pode-se! Por exemplo,(echo "this is stdout"; echo "this is stderr" >&2) > >(sed 's/.*/\x1b[32m&\x1b[0m/') 2> >(sed 's/.*/\x1b[31m&\x1b[0m/')
PM 2Ring
Observe que, se houver saída mista de stdout e stderr, o primeiro stdout será impresso e, em seguida, stderr.
nyuszika7h
5

O annotate-outputscript do Debiandevscripts permite que você faça isso seletivamente:

$ 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

A segunda coluna indica stdout e stderr com O e Erespectivamente.

Existem algumas advertências, a principal delas, conforme observado nas outras respostas: você não pode fazer isso depois do fato. Nem o shell nem o terminal sabem como um programa arbitrário usa seus descritores de arquivo, embora o shell seja responsável por configurá-los inicialmente.

Esse método usa fifos, escrever para um fifo pode se comportar de maneira diferente do que escrever para um tty, e escrever para dois fifos diferentes é definitivamente diferente (possíveis problemas de tempo / intercalação). Além disso, não é adequado para uso interativo, por exemplo, annotate-output bashnão é um ótimo plano, mas é útil para muitos outros fins. Existem muitos exemplos de scripts e funções de shell em respostas a perguntas relacionadas sobre a colorização de stdin / stdout / stderr. O mais robusto é stderrd, que usa a modificação do tempo de execução da (maioria) dos programas para modificar os dados gravados no stderr.

Esta pergunta à qual Anko se vincula tem boas respostas sobre esse tema relacionado: colorindo a saída stdout / stderr: Posso configurar meu shell para imprimir STDERR e STDOUT em cores diferentes?

mr.spuratic
fonte
1
Observe que é um bashscript usando while readloops e executando um datecomando para cada linha de stdout ou stderr, portanto será muito menos eficiente que o cmd > >(ts '%T O:') 2> >(ts '%T E:')equivalente.
Stéphane Chazelas
1

Além das outras respostas, é interessante apontar para /proc/$PID/fd(embora não responda à pergunta):

$ 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 você vê, aqui você pode ver os descritores de arquivo abertos para um processo. 0é o STDIN, 1é o STDOUTe 2é o STDERR. Se você não redirecionou o STDOUT ou o STDERR, verá/dev/pts/33 (pelo menos neste exemplo) porque eles apontariam para o terminal.

notas : /proc/$PIDexiste apenas para processos em execução. Nesse caso, usei catsem argumentos para que não termine até fechar o STDIN. Também o executei em segundo plano e, portanto, tenho o PID imediatamente por causa deste exemplo.

Carlos Campderrós
fonte
0

Não está claro o que você está perguntando, mas isso pode ajudar

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

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

Fonte

Se o código de saída for 0, significa simplesmente que o comando foi executado corretamente (stdout), aqui você pode encontrar significados se o código de saída for diferente de 0 (stderr)

klerk
fonte
Muito interessante! Eu nunca teria conectado status de saída com redirecionamento. Zero implica stdoute diferente de zero stderr?
KM.
7
@KM., Não. Eu diria que está fortemente correlacionado, mas não há nada que impeça um programa de gravar stderre retornar um bom código de erro ou de gravar stdoute retornar um código de erro incorreto. De fato, tente find /root- supondo que você não esteja executando como root, você deve imprimir 2 linhas - "/ root" será impresso stdoute "find: / root: Permissão negada" será impresso stderr. E find retorna um código de retorno incorreto.
23415 godlygeek
O problema do op é claro: como saber qual é stdout ou stderr.
it_is_a_literature
0

Normalmente, o STDERR terá o nome do programa anexado à mensagem com dois pontos.

Exemplo:

rpm -zq some_utils 
rpm: -zq: unknown option

Vs

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

Para capturar e testar a saída de erro:

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