【问题标题】:Results of printf() and system() are in the wrong order when output is redirected to a file [duplicate]将输出重定向到文件时, printf() 和 system() 的结果顺序错误[重复]
【发布时间】:2019-03-03 05:30:16
【问题描述】:

我有一个 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 的输出“这是一条测试消息”。

我觉得应该反过来。为什么会发生这种情况,我该怎么做才能使“这是一条测试消息”出现在 output.txt 的顶部?

如果重要的话,我对 C 和命令行都是新手。

【问题讨论】:

  • 当您混合不同的输出和输入缓冲区但不费心刷新输出时,无法保证它们会以什么顺序被写出。您的printf 输出可能会在ls 输出之前、之后甚至中间被写出!当然,在其他输出中间写出输出是很少见的,但我已经看到它发生了。直觉上,您认为“不可能将一行输出写在另一行中间”,但当您真正看到它发生时,您对现实的看法就会受到挑战。
  • 您需要冲洗打印的东西才能立即打印。

标签: c linux printf child-process io-redirection


【解决方案1】:

我怀疑这是因为标准输出缓冲区被刷新的顺序,这不一定是确定性的。父进程可能会产生 ls 进程,并且在返回之前不会刷新自己的标准输出。在进程退出之前,它可能不会真正刷新标准输出。

尝试在 printf 语句后添加fflush (stdout),看看是否会强制输出首先出现。

【讨论】:

    【解决方案2】:

    与输出缓冲有关。我设法重现了相同的行为。强制冲洗对我有用。

    #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;
    }
    

    在添加 fflush 之前:

    $ ./main > foo
    $ cat foo
    main
    main.c
    this is a test message.
    

    之后:

    $ ./main > foo
    $ cat foo
    this is a test message.
    foo
    main
    main.c
    

    【讨论】:

      【解决方案3】:

      默认情况下,当连接到终端时,stdout 的输出是行缓冲。也就是说,当缓冲区已满或添加换行符时,缓冲区会被刷新。

      然而,如果stdout 没有连接到终端,例如将程序的输出重定向到文件时发生的情况,那么stdout 将变为完全缓冲。这意味着缓冲区将被刷新并在其已满或显式刷新(程序退出时发生)时实际写入。

      这意味着从您的代码开始的单独进程的输出(就像您调用 system 时发生的情况一样)很可能首先被写入,因为该进程的缓冲区将在该进程结束时被刷新,即在你自己的过程之前。

      使用重定向(或管道)时会发生什么:

      1. 您的printf 调用写入stdout 缓冲区。
      2. system 函数启动一个新进程,该进程写入自己的缓冲区。
      3. 当外部进程(由您的system 调用启动)退出时,其缓冲区被刷新和写入。您自己的进程中自己的缓冲区不会被触及。
      4. 您自己的进程结束,您的stdout 缓冲区被刷新和写入。

      要以“正确”(或至少预期)顺序获得输出,请在调用 system 之前调用 fflush,以显式刷新 stdout,或在任何输出之前调用 setbuf 以完全禁用缓冲。

      【讨论】:

      • 出于好奇:即使在system 之后调用fflushsystem 完成所有操作(包括它自己的fflush),程序输出是否正确 ) 在那次通话中?
      • 另一个奇怪的问题——system() 的输出不也与printf() 之前使用的相同stdout 缓冲区结束吗?毕竟,它没有绕过它,直接写入控制台。两个程序的stdouts 都被链接了——这可以从两者都以output.txt 结尾的事实证明。
      • @Vilx-,不,stdio 是一个库,它使用的缓冲区存在于进程的内存中。 system() 使用单独的内存空间启动一个新进程,因此使用单独的缓冲区。但是,子进程确实从父进程继承了文件描述符,这就是输出到同一个文件的原因。操作系统级别的文件描述符和write()系统调用的区别; C 库 FILE 流和 printf() (等)在这里很重要。
      • 您的回答掩盖了一个可能 OP 应该注意的细节:C 程序中的 stdout 变量 包含一个指向 libc FILE 对象的指针,但是两个进程共享的是一个打开的文件描述符
      猜你喜欢
      • 1970-01-01
      • 2012-06-26
      • 2014-07-01
      • 2019-06-14
      • 2015-06-21
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多