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

发布于 2024-12-01 18:44:10 字数 600 浏览 0 评论 0原文

这里有很多与 fork() 和 exec() 相关的问题。不过,我还没有找到真正使使用它们的过程变得简单的方法,而让程序员的生活变得简单就是目标。

我需要一个 C++、Linux 友好的函数来执行以下操作:

string RunCommand(string command, string input){}

该函数应该能够运行 shell 命令,例如 grep,并将输入内容“通过管道”传输到其中并读取输出并返回它。因此,如果我在命令行执行以下操作:

ps -elf | grep somequerytext

我会在代码中执行以下操作:

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

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

*编辑:popen 被认为是一种简单的解决方案,但 popen 限制您只能通过管道输入数据或通过管道输出数据,但不能同时使用两者。

There are plenty of questions on here related to fork() and exec(). I have not found one that really makes the process of using them simple though, and making programmer's lives simple is the goal.

I need a C++, linux-friendly function that does the following:

string RunCommand(string command, string input){}

This function should be able to run a shell command, like grep, and "pipe" the content of input into it and read the ouptut and return it. So if I would do the following at the command line:

ps -elf | grep somequerytext

I would in code do:

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

*edit: The question is what is the best implementation of the RunCommand function.

*edit: popen was considered as a solution for simplicity, but popen restricts you to piping data in or piping data out, but not both.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

自此以后,行同陌路 2024-12-08 18:44:10

看来您需要一个函数来:

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

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

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

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

It appears that you need a function to:

  • Create two pipes and fork.
  • The child process then does:
    • Duplicate appropriate descriptors of the pipes so that one file descriptor is standard input and one standard output
    • Close the pipe descriptors
    • Split up the command string into arguments
    • Run the command with the arguments
  • The parent (main) process then does:
    • Close the appropriate pipe file descriptors
    • Writes the input string to the child and closes the pipe to the child's standard input
    • Reads the output string from the child
    • Closes the the pipe from the child's standard output
    • Waits for the child to die
  • When the child is dead, the main process can continue, returning the string that it read.

The only potential problem with this outline is if the child writes output before it is finished reading its input, and it writes so much output that the pipe is full (they have a finite and usually quite small capacity). In that case, the processes will deadlock - the parent trying to write to the child, and the child trying to write to the parent, and both stuck waiting for the other to read some data. You can avoid that by having two threads in the parent, one processing the writing, the other processing the reading. Or you can use two child processes, one to run the command and one to write to the standard input, while the parent reads from the command's standard output into a string.

One of the reasons there isn't a standard function to do this is precisely the difficulty of deciding what are the appropriate semantics.

I've ignored error handling and signal handling issues; they add to the complexity of it all.

千秋岁 2024-12-08 18:44:10

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

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

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

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

int RunCommand(string command)
{
    pid_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 负责解析其输入并通过在命令之间设置管道来运行命令。
如果您有兴趣了解 shell 的实现方式,请参阅 Terrence Chan 使用 C++ 进行 Unix 系统编程 第 8 章“Unix 进程”中的“A Minishell 示例”(Jonathan Leffler 的 答案几乎描述了一个shell实现!)

Before discussing the implementation of RunCommand, let us consider this code fragment:

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

In the above code fragment, the problem is that the commands are run sequentially, and does not run concurrently/in parallel. (See Programming with POSIX threads p.9 ) To give an example if ps -elf generates huge amount of data, that will be stored in psOutput and then passed to next command. But in actual implementation, each process in the pipe are run concurrently and data is passed with pipe (with some buffering of course) and there is no need to wait for the execution of one process before starting the execution of other process.

I suggest you to look into the Richard Steven's Advanced Programming in the Unix Environment chapter.8 "Process Control" p.223 for an implementation of system. Based on Richard Steven's code, a sample implementation of RunCommand will be as follows (just skeleton code, no error checking):

int RunCommand(string command)
{
    pid_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, ...);
    }
}

and then one would invoke the above functions as:

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

The shell takes care of parsing its input and running the commands by setting up pipes in between them.
If you are interested in understanding how a shell is implemented, see "A Minishell example" in Terrence Chan Unix System Programming using C++ chap.8 "Unix Processes" (Jonathan Leffler's answer pretty much describes a shell implementation!)

岁吢 2024-12-08 18:44:10

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

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

Why not use popen()? It's in the standard library, and very simple to use:

FILE* f = popen("ps -elf | grep somequerytext", "r");
char buf[2048];
buf[fread(buf, 1, 2048, f)] = '\0';
cout << buf;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文