当重定向输出时,如何强制程序刷新其标准输出?

11
我有一个闭源程序,它将输出打印到标准输出。我需要解析输出。所以我用dup2将输出重定向到一个FIFO中(在父进程中可以读取,该父进程又fork和exec二进制文件),然后执行程序。问题是文件中的fprintf调用现在因为写入文件而被缓冲了。
在调用exec之前,我尝试使用_IONBF参数在stdout上调用setvbuf函数,但问题仍然存在。
为什么setvbuf在我的情况下没有帮助?
如何强制刷新输出?
3个回答

7

setvbuf() 函数不会起到任何作用,因为它只改变了 C 运行库的一部分状态,而没有改变操作系统的一部分。当新进程开始运行时,它的 C 运行库将被重新初始化(如果它使用 CRT 的话!)

唯一我听说过的绕过这个问题的方法是通过某种方式模拟终端给进程。这是因为大多数 CRT 库默认情况下仅在它们认为自己连接到交互式终端时执行行缓冲(在 Unix 世界中:如果 isatty() 在文件描述符上返回 true),否则它们将缓冲更大的块(通常为8KB左右)。

这个实用工具 看起来是一个很好的起点。(摘自Trick an application into thinking its stdin is interactive, not a pipe的评论,其中还有其他有用的信息)


我派生了一个进程,在子进程中调用setvbuf,然后执行一个shell脚本,该脚本又执行另一个shell脚本,最终执行二进制文件。我能否更改最终的exec命令(bash),以便以某种方式禁用缓冲。 - Rohit Banga
1
我再次强调,在执行exec()之前调用setvbuf()是无用的——在exec()跨进程执行时,C运行库状态不会被保留!你最终要执行的进程甚至可能不会使用CRT!(虽然不太可能,但有可能。) - j_random_hacker
好的,我正在尝试使用pty方法。 请参阅http://stackoverflow.com/questions/2056858/cannot-write-to-pty-linux - Rohit Banga

3
我猜你的程序中有类似这样的代码(你可以为测试复制它,我在这里称其为isatty)。
#include <stdio.h>
#include <unistd.h>

const char* m1 = "%d: %s a TTY\n";

void isTty(FILE* f) {
    int fno = fileno(f);
    printf(m1, fno, (isatty(fno)) ? "is" : "is NOT");
}

int main(int argc, char* argv[]) {
    isTty(stdin);
    isTty(stdout);
}

例如,如果你运行它。
$ ./isatty
0: is a TTY
1: is a TTY

$ ./isatty > isatty.out
$ cat isatty.out 
0: is a TTY
1: is NOT a TTY

$ ./isatty > isatty.out < /dev/null
$ cat isatty.out 
0: is NOT a TTY
1: is NOT a TTY

现在,如果您创建了一个名为isatty.expectexpect脚本(如果未安装,请安装expect),请不要解释,并保留HTML标记。
#! /usr/bin/expect -f

spawn "./isatty"
expect

并运行它

$ ./isatty.expect 
spawn ./isatty
0: is a TTY
1: is a TTY

或者

$ ./isatty.expect > isatty.out 
$ cat isatty.out 
spawn ./isatty
0: is a TTY
1: is a TTY

3

unbuffer 工具可以帮助解决这个问题:

它是 expect-dev 的一部分,并且可以在 Ubuntu 中使用以下命令进行安装:

sudo apt-get install expect-dev

要使用它,请输入:

unbuffer ls > log.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接