用stringinput和输出运行进程

这里有很多关于fork()和exec()的问题。 我还没有find一个真正使它们简单易用的过程,而使程序员的生活变得简单就是目标。

我需要一个C ++,Linux友好的function,执行以下操作:

string RunCommand(string command, string input){} 

这个函数应该能够运行一个像grep这样的shell命令,并将input的内容“pipe”到它中,然后读取并返回它。 所以如果我在命令行上执行以下操作:

 ps -elf | grep somequerytext 

我会在代码做:

 string psOutput = RunCommand("ps -elf",""); string grepOutput = RunCommand("grep somequerytext", psOutput); 

*编辑:问题是什么是RunCommand函数的最佳实现。

*编辑:popen被认为是简单的解决scheme,但popen限制你pipe道数据或pipe道数据输出,但不是两个。

看来你需要一个功能来:

  • 创建两个管道和叉子。
  • 孩子的过程然后:
    • 复制管道的适当描述符,以便一个文件描述符是标准输入和一个标准输出
    • 关闭管道描述符
    • 将命令字符串拆分为参数
    • 使用参数运行命令
  • 父(主)过程然后:
    • 关闭适当的管道文件描述符
    • 将输入字符串写入子项并关闭管道到子项的标准输入
    • 从孩子读取输出字符串
    • 关闭孩子的标准输出的管道
    • 等待孩子死亡
  • 当孩子死了,主进程可以继续,返回它读取的字符串。

这个大纲唯一的潜在问题是,如果孩子在输入完成之前写入输出,并且写入了太多的输出以致管道已满(他们有一个有限的容量,而且容量通常很小)。 在这种情况下,进程将会死锁 – 试图写入子进程的父进程,以及试图写入父进程的子进程,并且都等待另一个读取一些数据。 您可以通过在父项中有两个线程来避免这种情况,一个处理写入,另一个处理读取。 或者,您可以使用两个子进程,一个用于运行命令,一个用于写入标准输入,而父级则从命令的标准输出读取字符串。

没有一个标准功能来做这件事的原因之一正是决定什么是适当的语义的困难。

我忽略了错误处理和信号处理问题; 他们增加了这一切的复杂性。

在讨论RunCommand的实现之前,让我们考虑这个代码片段:

 string psOutput = RunCommand("ps -elf",""); string grepOutput = RunCommand("grep somequerytext", psOutput); 

在上面的代码片段中,问题是这些命令按顺序运行,并不并行/并行运行。 (请参阅使用POSIX线程编程第 9页 )举例说明如果ps -elf生成大量数据,那么将存储在psOutput ,然后传递到下一个命令。 但是在实际的实现中,管道中的每个进程是并行运行的,数据是通过pipe传递的(当然有一些缓冲),不需要等待一个进程的执行,才能开始执行其他进程。

我建议你看看Richard Steven 在Unix环境下高级编程第8章“过程控制”第223页中的system实现。 基于Richard Steven的代码, RunCommand一个示例实现如下(只是框架代码,没有错误检查):

 int RunCommand(string command) { pit_t pid; if ( ( pid = fork() ) < 0 ) return -1; else if (pid == 0) { execl("/bin/sh", "sh", "-c", command.c_str(), (char*) 0); } else { /* The parent waits for the child */ wait(pid, ...); } } 

然后再调用上面的函数:

 string s("ps -elf | grep somequerytext"); int status = RunCommand(s); 

shell负责解析输入,并通过在它们之间设置pipe运行命令。 如果您有兴趣了解如何实现shell,请参阅Terrence Chan的“Minishell示例”。 使用C ++的Unix系统编程第8章“Unix进程”(Jonathan Leffler的回答非常详细地描述了shell的实现!

为什么不使用popen() ? 它在标准库中,使用非常简单:

 FILE* f = popen("ps -elf | grep somequerytext", "r"); char buf[2048]; buf[fread(buf, 1, 2048, f)] = '\0'; cout << buf;