为了描述这个问题,我创建了一个简单的 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 重定向到同一文件时如何保持输出顺序?
答案1
这是因为程序中的缓冲(在 C 库中)。默认行为是,如果 stdout 的输出进入终端,则它是行缓冲的;但如果它进入文件,则它是完全缓冲的。默认情况下,stderr 始终是无缓冲的。
因此,由于您要打印整行,因此行缓冲并不明显,但是当重定向到文件时,发送到 stdout 的输出将被缓冲,直到收集到整个块(几 kB),然后才实际写入。 (您也可以尝试看看如果打印部分行会发生什么。)
setbuf()
您可以使用/更改 C 程序内部的缓冲行为setvbuf()
,并使用该工具从外部更改缓冲行为stdbuf
(还有许多其他工具,请在站点上查找管道缓冲)。
setbuf(stdout, NULL)
例如,在程序开头添加将使标准输出无缓冲,并且运行它stdbuf -o0 ./a.out
也会这样做。但这会减慢程序的速度。
请注意,在最后一个示例中cat <(./a.out)
,只有程序的标准输出经过cat
,其标准错误直接进入终端。如果两者去到不同的地方,经过另外一个流程,就会造成重新排序。你需要cat <(./a.out 2>&1)
,或者./a.out 2>&1 | cat
让他们都经历一遍cat
。