使用popen()管道流调用循环shell命令行

3

我正在测试使用管道编程的示例,它看起来非常简单明了。

但是我想知道,如果第一个popen()调用的第一个参数(包含shell命令的字符串)是一个'while do'循环会发生什么。

例如,如果我执行此Shell命令3秒钟,我会得到以下输出:

tomas@ubuntu64:~$ while true; do ps -A; sleep 1; done | grep init
    1 ?        00:00:03 init
    1 ?        00:00:03 init
    1 ?        00:00:03 init

因此,grep在每次迭代中都在工作。

然而,如果我通过C语言示例来进行操作,将示例中的popen()更改为:

FILE* ps_pipe = popen("while true; do ps -A; sleep 1; done", "r");

当我执行已编译的C程序时,没有输出结果。

有人可以解释一下吗?


你是否真的从那个流中读取了数据?你读取的输出是用来做什么的,你期望看到什么结果?如果没有一个完整的自包含程序,很难确定可能出现了什么问题。 - Useless
3个回答

2

编辑:正如 J.F. Sebastian 发现的那样,默认情况下,当输出不定向到终端时,grep 使用大缓冲区。您需要使用选项 --line-buffered 才能立即获取输出(每行后)。

好的,我尝试了一下,它完美地工作了(感谢 J.F. Sebastian 的修复)。这是在 FreeBSD 9 上运行的完整代码:

#include <stdio.h>

int main() {

    char buffer[256];

    FILE *fd = popen("while true; do ps -A; sleep 1; done | grep --line-buffered init", "r");

        while(NULL != fgets(buffer, sizeof(buffer), fd)) {
            printf("GOT >%s<\n", buffer);
        }

    return 0;

}

由于我没有移除buffer末尾的\n,因此输出结果为:

GOT >   1 ??  ILs    0:00:02 /sbin/init --
>
GOT >1334 v0  R+     0:00:00 grep --line-buffered init
>
GOT >   1 ??  ILs    0:00:02 /sbin/init --
>
GOT >1334 v0  R+     0:00:00 grep --line-buffered init
>

@J.F.Sebastian 我知道这不是同一个命令,但它的作用是一样的...而且我懒得再次运行它。你可以随意编辑我的答案,提供精确的命令和相关输出,或者发表你自己的看法。 - Serge Ballesta
我已经发布了我的答案。我可以重现问题描述中的行为。在我的机器上(Ubuntu),添加grep确实有所不同。 - jfs
@J.F.Sebastian:哎呀,你说得对!我想我需要编辑这个 :-( 非常感谢你的注意... - Serge Ballesta

1
这是一个 块缓冲问题。当grep的stdout不是终端(tty),例如当它是管道(由popen()创建)时,它使用块缓冲而不是行缓冲。你不会看到任何东西,直到grep的stdout缓冲区溢出。添加--line-buffered参数到grep,你将立即看到输出(每秒一次)。

OP在运行FILE* ps_pipe = popen("while true; do ps -A; sleep 1; done", "r");时遇到了问题,他并没有遇到任何与grep相关的问题(这里有任何与grep相关的东西吗?!),他只是第一次尝试使用popen进行实验,并立即面临着无法获得输出的问题。然而,您错过了这一点,因此扣除了1分! - rakib_
@rakib:OP说:“当执行编译后的 C 程序时,我没有输出结果。”但是如果您在命令中运行 without grep,那么就没有块缓冲问题(至少在我的机器上),并且 OP 应该能够看到结果。另一方面,如果我使用 grep,那么我得不到结果(与 OP 相同)。这就是为什么 grep 的存在很重要的原因。我认为 OP 复制粘贴了他的代码,即他的实际代码中确实有 grep - jfs
OP在popen()内部没有尝试使用grep。为什么你不明白这一点?你误读了问题并含糊地添加了“--line-buffered”的解释。请重新阅读问题并尝试解决OP面临的问题,不要添加你的肤浅想法。 - rakib_
为什么我要重新阅读最后一句话?你的评论或者你表面上的想法对我来说并不重要,我们正在讨论的是问题本身,这才是最重要的。在我的回答中,你会发现原帖作者对于他的困惑(这表明他正在尝试学习这个概念)进行了评论,而你却试图注入自己的想法! - rakib_

-1

您所需要做的就是将输出写入标准I/O,因此请更改popen()参数如下:

          FILE* ps_pipe = popen("while true; do ps -A; sleep 1; done", "w");

我认为 OP 正在尝试从那个 shell 命令中读取输出,而不是写入其输入。 - Useless
@Useless 我认为OP说的是“当执行编译后的C程序时,我没有得到任何输出结果。”这是关于输出的问题,还是我错过了什么? - rakib_
好的,这样可以让它工作...但我担心我不明白为什么...我想我需要再学习一下 8-) - nephewtom
2
如果您从未在您的版本中实际阅读程序的输出,那么这将起作用,因为它现在共享您的父进程的标准输出。如果您想要将这些行读入您的父进程中,则需要使用popen进行读取(就像您所做的那样),然后实际读取。 - Useless
@Useless 问问楼主,不要问我。我看你对这个问题的理解有很大的困难,楼主正在学习并且感到困惑,看看他在我的回答后面的评论。如果那都不清楚,那就再见了。 - rakib_
显示剩余3条评论

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