Para descrever o problema, criei um programa simples em C.
root@u2004:~# cat test.c
#include <stdio.h>
int main(){
printf("output 1\n");
fprintf(stderr, "error 1\n");
printf("output 2\n");
fprintf(stderr, "error 2\n");
return 0;
}
root@u2004:~# gcc test.c
root@u2004:~#
Ao executá-lo, obtive esta saída:
root@u2004:~# ./a.out
output 1
error 1
output 2
error 2
root@u2004:~#
Como você pode ver, a ordem de saída no console é a mesma das instruções definidas no programa. Agora quero redirecionar o stderr e o stdout para um único arquivo e manter a ordem de saída. Abaixo está o que eu tentei:
root@u2004:~# ./a.out > out 2>&1
root@u2004:~# cat out
error 1
error 2
output 1
output 2
root@u2004:~#
root@u2004:~# cat <(./a.out)
error 1
error 2
output 1
output 2
root@u2004:~#
Como você pode ver, em ambos os casos, as mensagens de erro aparecem primeiro e depois a saída normal. Esta não é a mesma ordem definida no programa. Como manter a ordem de saída ao redirecionar stdout e stderr para o mesmo arquivo?
Isso é por causa do buffer no programa (na biblioteca C). O comportamento padrão é que a saída para stdout é armazenada em buffer de linha se for para um terminal, mas totalmente em buffer se for para um arquivo. E stderr é sempre sem buffer por padrão.
Portanto, como você está imprimindo linhas completas, o buffer de linha não é evidente, mas quando redirecionado para um arquivo, a saída que vai para stdout é armazenada em buffer até que um bloco inteiro seja coletado (alguns kB) e apenas escrito então. (Você também pode tentar ver o que acontece se você imprimir linhas parciais.)
Você altera o comportamento do buffer dentro do programa C com
setbuf()
/setvbuf()
, e do lado de fora com astdbuf
ferramenta (e várias outras, procure buffer de tubo no site).Por exemplo, adicionar
setbuf(stdout, NULL)
no início do seu programa tornaria o stdout sem buffer, e executá-lo comstdbuf -o0 ./a.out
também faria isso. Mas isso vai atrasar o programa.Observe que no seu último exemplo,
cat <(./a.out)
, é apenas o stdout do programa que passa porcat
, seu stderr vai direto para o terminal. Se os dois forem para lugares diferentes, por meio de outro processo, causará reordenação. Você precisariacat <(./a.out 2>&1)
, ou./a.out 2>&1 | cat
fazer com que ambos passemcat
.