为了描述这个问题,我创建了一个简单的 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:~#
当我运行它时,我得到了这个输出:
root@u2004:~# ./a.out
output 1
error 1
output 2
error 2
root@u2004:~#
如您所见,控制台中的输出顺序与程序中定义的语句相同。现在我想将 stderr 和 stdout 都重定向到一个文件,并保持输出顺序。以下是我尝试过的:
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:~#
如您所见,在这两种情况下,错误消息都会先出现,然后才是正常输出。这与程序中定义的顺序不同。将stdout和stderr都重定向到同一个文件时如何保持输出顺序?
这是因为程序中的缓冲(在 C 库中)。默认行为是,如果输出到终端,输出到 stdout 是行缓冲的,但如果输出到文件则完全缓冲。默认情况下,stderr 始终是无缓冲的。
因此,由于您要打印整行,因此行缓冲并不明显,但是当重定向到文件时,进入标准输出的输出会被缓冲,直到收集到整个块(几 kB),然后才实际写入。(您也可以尝试看看如果打印部分行会发生什么。)
setbuf()
您可以使用/更改 C 程序内部的缓冲行为setvbuf()
,并使用该stdbuf
工具从外部更改(以及其他一些工具,请在站点上查找管道缓冲)。例如
setbuf(stdout, NULL)
,在程序开头添加会使 stdout 无缓冲,并且运行它stdbuf -o0 ./a.out
也会这样做。但这会减慢程序的速度。请注意,在您的最后一个示例中
cat <(./a.out)
,它只是通过的程序的标准输出cat
,它的标准错误直接进入终端。如果两者去不同的地方,通过另一个过程,就会导致重新排序。你需要cat <(./a.out 2>&1)
,或者./a.out 2>&1 | cat
让他们都通过cat
。