Por que a saída de alguns programas Linux não é STDOUT nem STDERR?


21

Por que a saída de alguns programas Linux não é STDOUT nem STDERR?

Na verdade, quero saber como capturar de forma confiável toda a saída do programa, independentemente do 'fluxo' que ele usa. O problema que tenho é que alguns programas parecem não permitir que sua saída seja capturada.

Um exemplo é o comando 'time':

time sleep 1 2>&1 > /dev/null

real        0m1.003s
user        0m0.000s
sys         0m0.000s

ou

time sleep 1 &> /dev/null

real        0m1.003s
user        0m0.000s
sys         0m0.000s

Por que vejo a saída nas duas vezes? Eu esperava que tudo fosse canalizado para / dev / null .

Qual fluxo de saída está usando o tempo e como posso canalizá-lo para um arquivo?

Uma maneira de contornar o problema é criar um script Bash , por exemplo, combine.shcontendo este comando:

$@ 2>&1

Então a saída de 'time' pode ser capturada da maneira correta:

combine.sh time sleep 1 &> /dev/null

(nenhuma saída é vista - correta)

Existe uma maneira de conseguir o que eu quero sem usar um script combinado separado?


3
primeiro você deve reverter a ordem: 2>&1 > /dev/nullsignifica "2 agora vai para onde 1 vai (por exemplo, o terminal, por padrão), e então 1 agora vai para / dev / null (mas 2 ainda vai para o terminal!). use >/dev/null 2>&1para dizer "1 vai agora para / dev / null, depois 2 vai para onde 1 vai (ou seja, também para / dev / null). Isso ainda não funcionará aqui, pois o 'tempo' interno não será redirecionado, mas é mais geralmente correto (por exemplo, funcionaria se você usar / usr / bin / time). Pense em "2> & 1" como copiar a "direção" de 1 para 2, e não como 2, indo para 1
Olivier Dulac

Respostas:


38

Esta pergunta é abordada no BashFAQ / 032 . No seu exemplo, você:

{ time sleep 1; } 2> /dev/null

A razão porque

time sleep 1 2>/dev/null

não se comporta como você espera, porque com essa sintaxe, você desejará timeo comando sleep 1 2>/dev/null(sim, o comando sleep 1com stderr redirecionado para /dev/null). O builtin timefunciona dessa maneira para tornar isso realmente possível.

O bash builtin pode realmente fazer isso porque ... bem, é um builtin. Esse comportamento seria impossível com o comando externo timegeralmente localizado em /usr/bin. De fato:

$ /usr/bin/time sleep 1 2>/dev/null
$

Agora, a resposta para sua pergunta

Por que a saída de alguns programas linux não vai para STDOUT nem STDERR?

é: sim, a saída vai para stdout ou stderr .

Espero que isto ajude!


2
você pode criar outro fd e tem comandos explicitamente ir para aqueles (ex: no script bash: exec 3>/some/file ; ls >&3 ;)
Olivier Dulac

@OlivierDulac Claro, ou ainda mais simples com o coprocbuiltin. Mas não é o caso do timeembutido.
gniourf_gniourf

@ gniourf-gniourf: Eu estava comentando por causa de sua frase "a saída vai para stdout ou stderr" ^^
Olivier Dulac

14

A sua questão particular sobre timebuiltin foi respondida, mas não são alguns comandos que não escrevem tanto para stdoutou para stderr. Um exemplo clássico é o comando Unix crypt. cryptsem argumentos criptografa a entrada padrão stdine a grava na saída padrão stdout. Ele solicita ao usuário uma senha usando getpass(), que por padrão gera um prompt para /dev/tty. /dev/ttyé o dispositivo terminal atual. Gravar em /dev/ttytem o efeito de gravar no terminal atual (se houver um, consulte isatty()).

O motivo pelo qual cryptnão stdouté possível gravar é porque ele grava a saída criptografada stdout. Além disso, é melhor solicitar, em /dev/ttyvez de escrever, para stderrque, se um usuário redirecionar stdoute stderr, o prompt ainda seja visto. (Pelo mesmo motivo, cryptnão é possível ler a senha stdin, pois está sendo usada para ler os dados para criptografar.)


2
+1. Menos relevante para o OP, mas mais relevante para todos os que se deparam com "Por que a saída de alguns programas Linux não vai para STDOUT nem STDERR?" via Google. :-)
ruakh

0

time sleep 1 > /dev/null 2>&1# redireciona a saída "sleep" para nulo. Então, "time" grava sua própria saída, sem redirecionamento. É como " time (sleep 1 > /dev/null 2>&1)".

(time sleep 1) > /dev/null 2>&1 # executa "time sleep 1" e redireciona sua saída para null.

[] s


-1

O problema no seu caso é que o redirecionamento funciona de outra maneira. Você escreveu

time sleep 1 2>&1 > /dev/null

Isso redireciona a saída padrão para /dev/nulle, em seguida, redireciona o erro padrão para a saída padrão.

Para redirecionar toda a saída, você deve escrever

time sleep 1 > /dev/null 2>&1 

Em seguida, o erro padrão será redirecionado para a saída padrão e, depois disso, toda a saída padrão (contendo o erro padrão) será redirecionada para /dev/null.


Isso não funciona com o bash embutido time . Veja minha resposta para algumas explicações.
Gnourf_gniourf

+1 porque esta é uma resposta útil para uma pergunta semelhante. Embora o @Olivier o explique melhor no comentário da pergunta acima.
Will Sheppard
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.