【发布时间】:2017-10-17 22:51:54
【问题描述】:
我的问题 在哪里可以找到一个清晰的 C 代码示例,该示例在 MacOS 上设置和使用带有 popen() 的双向管道?
我正在尝试使用 popen() 在我的 C 程序和 Mac OS X 上的外部程序之间打开双向管道。
我的 C 程序不得不重复:
- 从自己的标准输入读取输入行
- 将其重新格式化为外部程序的输入
- 调用外部程序,将格式化的输入传递到外部程序的标准输入中
- 从外部程序的标准输出中读取结果
- 处理这些结果并在其自己的标准输出中生成一些诊断输出
如果我使用实际文件来存储输入/输出,然后使用 system() 或类似方法,我可以轻松做到这一点,但这太慢了,因为我有数十亿个输入。所以我想完全在内存中完成。
相关的手册页,以及本组和其他人的互联网讨论表明,我可以打开一个到外部程序的管道,然后用 fprintf 写入它,就像写入文件一样。
pout = popen(external_program, "w")
fprintf(pout,input_to_external_program);
这可以作为单向管道正常工作,将格式化的输入馈送到外部程序中。
显然,在 Mac OS 上可以通过指定“r+”而不是“r”或“w”作为 popen 的相关参数来打开双向管道,从而使用同一管道读取和写入程序。
pout = popen(external_program, "r+")
fprintf(pout,"%s", input_to_external_program);
fscanf(fpout,"%s", &output_from_external_program);
我不需要在读取、写入、读取、写入等之间交替,因为我的程序将在开始读取外部程序的输出之前完成将输入写入外部程序。
外部程序应该只读取一个独立的输入序列(即,它以一个特殊的行结束,表示“输入结束”),然后运行到完成,同时写入输出,然后终止。
但是当我尝试使用这个功能时,它就不起作用了,因为有些东西阻塞了 - 外部程序启动了,但甚至没有完成一个输入。
即使我只是忘记阅读任何内容,而只是将“w”替换为“r+”,那么之前工作的功能也会停止工作。
我今天早上的大部分时间都在搜索可以修改的工作 popen() 示例,但我发现的所有示例都只使用单向管道。
所以我们最终回到
我的问题 *我在哪里可以找到一个清晰的 C 代码工作示例,该示例在 MacOS 上设置并使用带有 popen() 的双向管道?**
如果它更容易,我也许可以用 Perl 替换 C 部分。
更新
感谢您的回答,但我仍然无法使其正常工作,尽管我可以使一些小规模版本工作。例如,我编写了一个小程序,它会在输入的任何数字上加一,如果输入为 -99,则退出/我称之为“复制”。
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char **argv) {
int k;
setbuf(stdout,NULL);
while (scanf("%d",&k) == 1) {
if (k == -99) break;
printf("%d\n",k+1);
}
}
然后我编写了一个父程序,它将创建管道,向其中写入一些东西,然后等待输出(不做 NOHANG,因为我希望父级继续吸取子级的输出,直到子级完成)。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
FILE *p;
int k;
p = popen("copy", "r+");
if (!p) {
puts("Can't connect to copy");
return 1;
}
fprintf(p,"%d\n",100);
fprintf(p,"%d\n",200);
fprintf(p,"%d\n",300);
fprintf(p,"%d\n",-99);
fflush(p);
while (fscanf(p,"%d",&k) == 1) {
printf("%d\n", k);
}
pclose(p);
return 0;
}
这很好用,但是一旦我用我的实际子进程替换虚拟程序“副本”,它就会挂起。
一切都应该是一样的——父进程为子进程创建一个完整的输入序列,子进程读取它,做一些工作,输出任意数量的输出行,然后终止。
但有些不同 - 要么子进程没有接收到它的所有输入,要么正在缓冲它的输出或其他东西。神秘。
【问题讨论】:
-
也许检查一下,它可能会给双向管道一些提示:stackoverflow.com/questions/8390799/…
-
popen()手册页的摘录。 “popen() 函数通过创建管道、分叉和调用 shell 来打开一个进程。由于管道根据定义是单向的,因此类型参数可能只指定读取或写入,而不是两者;生成的流相应地是只读的或只写。” -
如果你想要双向通信,而不是
popen(),请使用fork(),在子进程中,在'执行'bc进程之前,修改stdin和@987654331 @ -
在贴出的代码中,
fpout来自哪里? -
@user3629249:在 macOS 上不是这样。相关man page的摘录:“由于popen()现在使用双向管道实现,类型参数可能请求双向数据流”