双向popen()在C中的Mac OS X上可以使用吗? [英] Does bidirectional popen() work on Mac OS X in C?

查看:119
本文介绍了双向popen()在C中的Mac OS X上可以使用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题 在哪里可以找到一个清晰的C代码示例,该示例在MacOS上使用带有popen()的双向管道进行设置并使用双向管道?

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

I am trying to use popen() to open a bidirectional pipe between my C program and an external program on Mac OS X.

我的C程序必须反复:

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

如果我使用实际文件存储输入/输出然后使用system()或类似文件,则可以轻松完成此操作,但这太慢了,因为我有数十亿的输入.所以我想完全在内存中做到这一点.

I can do this easily if I use an actual file to store the input/output and then use system() or similar, but this is too slow, as I have billions of inputs. So I'd like to do it entirely in memory.

相关的手册页以及该小组和其他小组中的Internet讨论表明,我可以打开一个通往外部程序的管道,然后像写入文件一样,使用fprintf对其进行写入.

The relevant man page, and Internet discussion in this group and others show that I can open a pipe to the external program, then write to it with fprintf just like writing to a file.

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

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

This works fine as a 1-directional pipe, feeding the formatted inputs INTO the external program.

显然,在Mac OS上,可以通过指定"r +"而不是"r"或"w"作为要打开的相关参数来打开双向管道,从而使用同一管道读写程序. /p>

Apparently it is possible on Mac OS to open a bidirectional pipe by specifying "r+" instead of "r" or "w" as the relevant argument to popen, and thereby read and write to the program using the same pipe.

pout = popen(external_program, "r+")
fprintf(pout,"%s", input_to_external_program);
fscanf(fpout,"%s", &output_from_external_program);

我不需要在读,写,读,写等之间切换,因为所有需要发生的事情是,我的程序将在开始从外部程序读取输出之前完成将输入写入外部程序的操作.

I do not need to alternate between reading, writing, reading, writing etc as all that needs to happen is that my program will finish writing the input to the external program before it starts reading the output from the external program.

外部程序应该只读取一个自包含的输入序列(即,以特殊行结尾以指示输入结束"),然后运行完成,随即写入输出,然后终止.

The external program should just read one self-contained input sequence (i.e. it ends with a special line to indicate "end of input"), and then run to completion, writing output as it goes, then terminating.

但是当我尝试使用此功能时,它只是不起作用,因为某些东西被阻止了-外部程序启动,但是甚至不能完成一个输入.

But when I try to use this feature, it just doesn't work, because something blocks - the external program starts, but does not complete even one input.

即使我只是忘了读任何书,而只是用"r +"代替"w",以前使用的功能也会停止工作.

Even when I just forget about doing any reading, but just replace "w" with "r+" then the previously-working functionality stops working.

我今天上午大部分时间都在搜索一个可以修改的popen()示例,但发现的所有示例仅使用单向管道.

I have searched for most of this morning for a working popen() example that I can modify, but all that I have found only use a unidirectional pipe.

所以我们最后回到了

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

My Question *Where can I find a clear working example of C code that sets up and uses a bidirectional pipe with popen() on MacOS?**

如果更容易,我可以用Perl替换C部分.

If it makes it easier, I might be able to replace the C part with Perl.

更新

感谢您的回答,但是尽管我可以使某些小版本运行,但仍无法使其正常工作.例如,我编写了一个小程序,该程序只会在输入的所有数字上加一个,如果输入为-99,则退出,否则我将其称为副本".

Thanks for the answers, but I am still unable to make it work properly though I can make some small-scale versions work. For example, I wrote a little program that would just add one to any numbers fed to it, quitting if the input is -99/ I called this "copy".

#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,因为我希望父级继续吸收子级的输出,直到子级完成为止) ).

Then I wrote a parent program that will create the pipe, write some stuff to it, and then wait for the output (not doing NOHANG because I want the parent to keep sucking up the output of the child until the child is done).

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

这很好用,但是只要我用实际的子进程替换虚拟程序"copy",它就会挂起.

This works perfectly, but as soon as I replace the dummy program "copy" with my actual subprocess, it just hangs.

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

Everything should be the same - the parent process creates an entire input sequence for the child, which reads it, does some work, outputs some arbitrary number of lines of output and then terminates.

但是有所不同-子进程未收到其所有输入,或者正在缓冲其输出或其他内容.神秘.

But something is different - either the child process is not receiving all of its input, or is buffering its output or something. Mysterious.

推荐答案

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

Here's a program that interacts with bc to perform integer calculations. In the simplest case, all you need to do is use fgets() and fputs() to read and write to/from the pipe.

但是,如果输入不产生任何输出的内容,例如设置变量(例如x=1)或运行时错误(例如1/0),则fgets()将无限期地等待输入.为避免这种情况,您必须使管道无阻塞,并反复检查管道以获取管道过程的输出. /p>

However, if you enter something that produces no output, like setting a variable (e.g., x=1) or a runtime error (e.g., 1/0), then fgets() will continue waiting for input indefinitely. To avoid this situation, you have to make the pipe non-blocking and check it repeatedly for output from the piped process.

#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()在输入的末尾包含换行符.校正:管道被 block 缓冲,因此在将数据发送到fflush()后调用fflush()管道.

Also, don't forget that pipes are generally line buffered. This isn't a problem here because fgets() includes a newline character at the end of the input. Correction: pipes are block buffered, so call fflush() after sending data into the pipe.

这篇关于双向popen()在C中的Mac OS X上可以使用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆