在C程序中执行另一个程序

8

我应该如何在我的 C 程序中运行另一个程序?我需要能够将数据写入启动程序的 STDIN(也许从它的 STDOUT 中读取)。

我不确定这是否是标准的 C 函数。我需要适用于 Linux 的解决方案。

6个回答

17
您想使用 popen。它提供了一个单向管道,您可以通过它访问程序的标准输入和标准输出。

popen 在现代 Unix 和类 Unix 操作系统上是标准的,其中 Linux 就是其中之一 :-)

类型


popen 在现代 Unix 和类 Unix 操作系统上是标准的,Linux 是其中之一。
man popen

在终端中输入命令以了解更多信息。

编辑

无论 popen 生成单向还是双向管道都取决于实现方式。 在 LinuxOpenBSD 中,popen 生成单向管道,只能读或只能写。在 OS XFreeBSDNetBSD 中,popen 生成双向管道。


1
请注意,这是标准输入或标准输出,而不是同时两者。 - wnoise
在Ubuntu 9.04上,"man popen"显示:"由于管道的定义是单向的,类型参数只能指定读取或写入,而不能同时指定。" - Brent Bradburn
@freespace难怪他们这么做。 "单向"意味着只有一个方向,而不是两个方向。你提出了关于双向管道的主张,你能用证据支持吗? - n. m.
@n.m. 抱歉,我在评论中打错了一个字。我本意是说双向的。我提供的链接也没什么用,因为它没有直接链接到OS X的popen手册页。更好的链接是https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/popen.3.html。我会更新我的答案,指出Linux和*BSD衍生版之间的差异。 - freespace
哇,这非常有趣,但它确实只限于BSD系统。POSIX不支持它。 - n. m.
显示剩余3条评论

11

我一段时间前为某人编写了一些示例C代码,展示如何完成此操作。以下是该代码给您参考:

在这里:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>

void error(char *s);
char *data = "Some input data\n";

main()
{
  int in[2], out[2], n, pid;
  char buf[255];

  /* In a pipe, xx[0] is for reading, xx[1] is for writing */
  if (pipe(in) < 0) error("pipe in");
  if (pipe(out) < 0) error("pipe out");

  if ((pid=fork()) == 0) {
    /* This is the child process */

    /* Close stdin, stdout, stderr */
    close(0);
    close(1);
    close(2);
    /* make our pipes, our new stdin,stdout and stderr */
    dup2(in[0],0);
    dup2(out[1],1);
    dup2(out[1],2);

    /* Close the other ends of the pipes that the parent will use, because if
     * we leave these open in the child, the child/parent will not get an EOF
     * when the parent/child closes their end of the pipe.
     */
    close(in[1]);
    close(out[0]);

    /* Over-write the child process with the hexdump binary */
    execl("/usr/bin/hexdump", "hexdump", "-C", (char *)NULL);
    error("Could not exec hexdump");
  }

  printf("Spawned 'hexdump -C' as a child process at pid %d\n", pid);

  /* This is the parent process */
  /* Close the pipe ends that the child uses to read from / write to so
   * the when we close the others, an EOF will be transmitted properly.
   */
  close(in[0]);
  close(out[1]);

  printf("<- %s", data);
  /* Write some data to the childs input */
  write(in[1], data, strlen(data));

  /* Because of the small amount of data, the child may block unless we
   * close it's input stream. This sends an EOF to the child on it's
   * stdin.
   */
  close(in[1]);

  /* Read back any output */
  n = read(out[0], buf, 250);
  buf[n] = 0;
  printf("-> %s",buf);
  exit(0);
}

void error(char *s)
{
  perror(s);
  exit(1);
}

8
  1. 使用pipe(...)创建两个管道,一个用于stdin,另一个用于stdout
  2. fork(...)进程。
  3. 在子进程中(fork(...)返回0的进程),将管道复制到stdin/stdout中,使用dup (...)
  4. 在子进程中使用exec[v][e]启动要运行的程序文件。
  5. 在父进程中(fork返回子进程的PID),使用循环从子进程的stdout中读取数据(使用select(...)poll(...)read(...)),直到子进程终止(使用waitpid(...))。
  6. 如果子进程需要输入,最终向其提供stdin上的输入。
  7. 完成后close(...)管道。

5

对于简单的单向通信,popen()是一个不错的解决方案。但是对于双向通信来说,它没有用处。

在我看来,imjorge(Jorge Ferreira)为双向通信提供了大部分答案(80%?),但遗漏了一些关键细节。

  1. 父进程关闭用于向子进程发送消息的管道的读端非常重要。
  2. 子进程关闭用于向子进程发送消息的管道的写端非常重要。
  3. 父进程关闭用于向父进程发送消息的管道的写端非常重要。
  4. 子进程关闭用于向父进程发送消息的管道的读端非常重要。

如果您不关闭未使用的管道端口,则在其中一个程序终止时,您将无法获得合理的行为;例如,子进程可能正在从其标准输入中读取数据,但除非在子进程中关闭管道的写入端口,否则它永远不会收到EOF(从read获取的零字节),因为它仍然保持着该管道的打开状态,系统认为它可能随时写入该管道,即使它目前已经挂起等待从中读取数据。

写入进程应考虑是否处理SIGPIPE信号,该信号会在您在没有读取进程的管道上写入数据时发出。

您必须注意管道容量(平台相关,可能只有4KB)并设计程序以避免死锁。


1

0

我认为你可以使用

freopen

来实现这个。


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