双向popen()是否适用于C中的Mac OS X?

时间:2017-05-18 08:26:19

标签: c pipe popen

我的问题 在哪里可以找到一个明确的C代码示例,它在MacOS上设置和使用带有popen()的双向管道?

我正在尝试使用popen()在我的C程序和Mac OS X上的外部程序之间打开双向管道。

我的C程序必须重复:

  • 从自己的标准输入读取输入行
  • 将其重新格式化为外部程序的输入
  • 调用外部程序,将格式化的输入传递给外部程序的stdin
  • 从外部程序的标准输出中读取结果
  • 处理这些结果并在其自己的标准输出上生成一些诊断输出

如果我使用实际文件存储输入/输出然后使用system()或类似文件,我可以很容易地做到这一点,但这太慢了,因为我有数十亿输入。所以我想在记忆中完全做到这一点。

相关的手册页,以及该组和其他人的互联网讨论显示我可以打开外部程序的管道,然后使用fprintf写入它,就像写入文件一样。

pout = popen(external_program, "w")
fprintf(pout,input_to_external_program);

这可以作为单向管道正常工作,将格式化的输入输入INTO外部程序。

显然,在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;
 }

这很有效,但是一旦我更换了虚拟程序&#34; copy&#34;与我的实际子进程一样,它只是挂起。

一切都应该是相同的 - 父进程为子进程创建一个完整的输入序列,它读取它,做一些工作,输出一些任意数量的输出行然后终止。

但是有些东西是不同的 - 要么子进程没有接收到它的所有输入,要么缓冲它的输出或其他东西。神秘。

2 个答案:

答案 0 :(得分:4)

这是一个与bc交互以执行整数计算的程序。在最简单的情况下,您需要做的就是使用fgets()fputs()来读取和写入管道。

但是,如果输入的内容不产生输出,例如设置变量(例如x=1)或运行时错误(例如1/0),那么fgets()将继续等待无限期输入。为了避免这种情况,您必须制作管道non-blocking并重复检查管道进程的输出。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main(void) {
    FILE *p;
    char buf[1025];
    int i, fd, delay;

    p = popen("bc", "r+");
    if (!p) {
        puts("Can't connect to bc");
        return 1;
    }
    /* Make the pipe non-blocking so we don't freeze if bc produces no output */
    fd = fileno(p);
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);

    puts("Give me a sum to do (e.g., 2+2):");
    if (fgets(buf, 1024, stdin)) {
        fputs(buf, p);
        fflush(p);
        for (delay=1,i=0; i<20; delay*=2,i++) {
            usleep(delay);
            if (fgets(buf, 1024, p)) {
                printf("The answer is %s", buf);
                break;
            }
        }
    }
    pclose(p);

    return 0;
}

另外,不要忘记管道通常是行缓冲的。这不是问题,因为fgets()在输入的末尾包含换行符。更正:管道缓冲,因此调用fflush()将数据发送到管道后。

答案 1 :(得分:1)

确保父进程中fflush(pout)以及子进程在每次输出后fflush(stdout)执行{。}}。