使用字符串输入和输出运行进程

时间:2011-08-28 04:18:58

标签: c++ linux exec fork pipe

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

我需要一个C ++,linux友好的函数,它可以执行以下操作:

string RunCommand(string command, string input){}

这个函数应该能够运行一个shell命令,比如grep,并“输入”输入内容并读取ouptut并返回它。因此,如果我在命令行执行以下操作:

ps -elf | grep somequerytext

我会在代码中执行:

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

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

*编辑:popen被认为是一个简单的解决方案,但是popen限制你管道数据或管道输出数据,但不是两者兼而有之。

3 个答案:

答案 0 :(得分:4)

您似乎需要一个功能:

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

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

没有标准函数来执行此操作的原因之一正是难以确定什么是适当的语义。

我忽略了错误处理和信号处理问题;它们增加了复杂性。

答案 1 :(得分:4)

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

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

在上面的代码片段中,问题是命令是按顺序运行的,并不是并发/并行运行的。 (参见使用POSIX线程进行编程 p.9)如果ps -elf生成大量数据,请提供示例,该数据将存储在psOutput中,然后传递给下一个命令。但在实际实现中,管道中的每个进程都是并发运行的,数据是以pipe传递的(当然还有一些缓冲),在开始执行其他进程之前无需等待一个进程的执行。

我建议您查看Richard Steven在Unix环境中的高级编程第8章“过程控制”p.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示例” Unix系统编程使用C ++ 第8章“Unix进程”(Jonathan Leffler的answer几乎描述了一个shell实现!)

答案 2 :(得分:0)

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

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