Obter stdout e stderr mesclados e separados

4

Estou procurando uma maneira de executar um programa Prog e terminar com 3 arquivos:

  1. O stdout de Prog
  2. O stderr de Prog
  3. Ambos stdout e stderr de Prog combinados como seriam na tela se nenhum redirecionamento ocorresse.

Existe uma combinação de redirecionamento, canais, etc., que poderia conseguir isso?

Nota: eu normalmente uso bash.

    
por Angew 10.12.2012 / 09:45

3 respostas

4
Até onde eu sei, não há uma maneira elegante de fazer isso (para uma maneira deselegante que funciona, mas não é bonita, vá até o final da minha resposta). Duvido que você possa fazer melhor do que:

$ command >stdout.txt 2>stderr.txt && cat stdout.txt stderr.txt > both.txt

Existem vários legal truques você pode usar, mas nenhum deles parece ter sucesso em produzir os 3 arquivos melhor do que o anterior. O principal problema é que o arquivo both.txt não mostrará as mensagens (STDERR e STDOUT) na ordem correta. Isso ocorre porque (como explicado aqui ):

When you redirect both standard output and standard error to the same file, you may get some unexpected results. This is due to the fact that STDOUT is a buffered stream while STDERR is always unbuffered. This means that every character of STDERR is written as soon as it is available while STDOUT writes stuff in batches. When both STDOUT and STDERR are going to the same file you may see error messages appear sooner than you would have expected them in relation to the actual output of your program or script. It isn’t anything to be alarmed about but is simply a side-effect of buffered vs. unbuffered streams, you just need to keep it in mind.

A melhor alternativa que eu consegui encontrar foi o uso de subshells bash, é meio complexo e still não exibe a saída na ordem correta. Fiz um script Perl simples, test.pl que imprime "OUT" para STDOUT e "ERR" para STDERR , repetindo o processo 3 vezes:

#/usr/bin/perl 
for($i=0; $i<=2; $i++){
    print STDOUT "OUT\n"; 
    print STDERR "ERR\n"
}

Sua saída normal e não redirecionada é:

$ ./test.pl
OUT
ERR
OUT
ERR
OUT
ERR

Para redirecionar a (s) saída (s) que eu executei:

(./test.pl 2> >(tee error.txt) > >(tee out.txt)) > both.txt 

Isso usa tee , um programa que imprimirá sua entrada para a tela e para um nome de arquivo. Portanto, estou redirecionando STDERR e passando-o como entrada para tee , solicitando que ele grave no arquivo error.txt . Da mesma forma, com STDOUT e o arquivo out.txt . Eu estou colocando a coisa toda em um subshell ( (...) ) para que eu possa capturar all de sua saída e redirecionar para both.txt .

Agora, isso funciona na medida em que cria três arquivos, um com STDERR , um com STDOUT e um com ambos. No entanto, conforme explicado acima, isso resulta em mensagens que aparecem na ordem incorreta em both.txt :

$ cat both.txt 
ERR
ERR
ERR
OUT
OUT
OUT

A única maneira de contornar isso foi encontrar o tempo que foi impresso em cada linha de saída e, em seguida, a classificação, mas ele está ficando seriamente complicado e, em seu lugar, eu perguntaria: eu mesmo se realmente vale a pena:

 $(./test.pl \
   2> >(while read n; do echo 'date +%N'" $n"; echo "$n" >>error.txt; done) \
    > >(while read n; do echo 'date +%N'" $n"; echo "$n" >> out.txt; done )) \
 | gawk '{print $2}'> both.txt 
    
por 11.12.2012 / 17:09
2

1) crie dois scripts:

getout.sh
geterr.sh

getout.sh receberá o Stdout.
O script gravará dados em f1.txt e também gravará os mesmos dados no arquivo f12.txt

geterr.sh receberá o Stderr.
O script gravará dados em f2.txt e também gravará os mesmos dados no arquivo f12.txt

Não acredito que seja possível preservar com segurança a ordem exata dos dados gravados no arquivo: f12.txt. Isso ocorre porque as gravações no Stdout são armazenadas em buffer pelo sistema operacional e as gravações no Stderr acontecem quase em tempo real. Isso pode (irá) atrasar os dados do Stdout de serem vistos pelo script. O efeito disso é:

a. For some data written to Stdout BEFORE data written to 
   Stderr, the data written to f12.txt from Stderr may 
   precede the data written by Stdout.  
b. Some data written to Stdout will be accumulated with 
   other data written earlier or later. As the script sees it, 
   these data lines may all appear at the same time (together).  
c. Because of (b), for some data written to Stderr in-between 
   multiple data writes to Stdout, the data written to f12.txt 
   from Stderr may precede the whole group of data written to Stdout.  
d. Because of all this, including timestamps with the data 
   written to f12.txt will not preserve the order that events happened.  

2) crie 2 pipes nomeados:

% mknod pout p
% mknod perr p

3) deixe os scripts "ouvirem" os canais:

% ./getout.sh < pout &
% ./geterr.sh < perr &

4) Comece seu programa (Prog) assim:

% Prog 1> pout 2> perr
    
por 11.12.2012 / 05:35
0

Para separá-los em arquivos diferentes:

./Prog 1> stdout.log 2> stderr.log

Para que eles sejam combinados no mesmo arquivo:

./Prog 1>> combine.log 2>> combine.log

Existe uma maneira 'abreviada' de fazer a combinação para o bash, mas o acima é mais seguro no caso de você usar sh.

Depende de quão precisa sua combinação precisa, o seguinte pode ou não funcionar

touch stdout.log
touch stderr.log
tail -f stdout.log >> combine.log &
tail -f stderr.log >> combine.log &
./Prog 1>> stdout.log 2>> stderr.log ; pkill tail
    
por 10.12.2012 / 18:49