C 将输出重定向到文件时,printf()和system()的结果顺序错误
我有一个C程序,可以编译成一个名为myprogram的可执行文件。这是它的主要功能:C 将输出重定向到文件时,printf()和system()的结果顺序错误,c,linux,printf,child-process,io-redirection,C,Linux,Printf,Child Process,Io Redirection,我有一个C程序,可以编译成一个名为myprogram的可执行文件。这是它的主要功能: int main(int argc, char ** argv) { printf("this is a test message.\n"); system("ls"); return 0; } 当我在Linux shell中运行myprogram>output.txt并检查output.txt时,我看到上面列出的ls的输出“这是一条测试消息” 我觉得应该是另一种方式。为什么会发生这种情况,我该
int main(int argc, char ** argv) {
printf("this is a test message.\n");
system("ls");
return 0;
}
当我在Linux shell中运行myprogram>output.txt
并检查output.txt时,我看到上面列出的ls
的输出“这是一条测试消息”
我觉得应该是另一种方式。为什么会发生这种情况,我该怎么做才能使“这是一条测试消息”出现在output.txt的顶部
如果有关系的话,我对C和命令行都是新手。我怀疑这是因为标准输出缓冲区刷新的顺序,这不一定是确定的。父进程可能会生成
ls
进程,直到返回后才刷新自己的标准输出。在进程退出之前,它可能不会实际刷新标准输出
尝试在printf语句之后添加
fflush(stdout)
,看看这是否会强制首先显示输出。它与输出缓冲有关。我设法重现了同样的行为。强迫冲水帮了我的忙
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv) {
printf("this is a test message.\n");
fflush(stdout);
system("ls");
return 0;
}
及之后:
$ ./main > foo
$ cat foo
this is a test message.
foo
main
main.c
默认情况下,
stdout
的输出在连接到终端时是行缓冲的。也就是说,当缓冲区已满或添加换行符时,会刷新缓冲区
但是,如果stdout
未连接到终端,如将程序输出重定向到文件时所发生的情况,则stdout
将被完全缓冲。这意味着当缓冲区已满或显式刷新(程序退出时发生)时,缓冲区将被刷新并实际写入
这意味着从代码开始的单独进程的输出(如调用system
时发生的情况)最有可能首先写入,因为该进程结束时,该进程的缓冲区将被刷新,这是在您自己的进程之前
使用重定向(或管道)时会发生什么情况:
printf
调用将写入stdout
缓冲区system
函数启动一个新进程,该进程将写入自己的缓冲区系统
调用启动)退出时,将刷新并写入其缓冲区。在您自己的流程中,您自己的缓冲区不会被触及stdout
缓冲区被刷新和写入要以“正确”(或至少预期)的顺序获得输出,请在调用
system
之前调用,以显式刷新stdout
,或在任何输出之前调用以完全禁用缓冲。出于好奇:即使在system
之后调用fflush
,程序输出是否正确,或者系统
在该调用中执行所有操作(包括它自己的fflush
)?另一个奇怪的问题是system()
的输出不也会在以前使用的stdout
缓冲区中结束吗?毕竟,它并没有绕过它直接写入控制台。这两个程序的stdout
s都是链接的,这一点很明显,因为它们都在output.txt
@Vilx-,不,stdio
是一个库,它使用的缓冲区存在于进程的内存中system()
使用单独的内存空间启动新进程,从而使用单独的缓冲区。但是,子进程确实从父进程继承了文件描述符,这就是为什么输出会转到同一个文件。操作系统级文件描述符与write()
系统调用之间的差异;C库文件流和printf()
(等)在这里很重要。您的答案掩盖了OP可能应该注意的一个细节:C程序中的stdout
变量包含指向libc文件
对象的指针,但这两个进程共享的是一个开放的文件描述符。当您混合使用不同的输出和输入缓冲区,但不必刷新输出时,无法保证它们的写入顺序。您的<代码> Prtff<代码>输出可以在<>代码> L< <代码>输出中间写入,输出!当然,输出在其他输出中间的输出是罕见的,但是我看到了它的发生。直观地说,你认为“一条输出线无法在另一条线中间写出”,但是当你真的看到它发生时,你对现实的感知就受到了挑战。你需要冲洗打印出来的东西才能立即打印出来。
$ ./main > foo
$ cat foo
this is a test message.
foo
main
main.c