Linux:使用管道 stdin/stdout 执行子进程
- 2024-10-11 08:36:00
- admin 原创
- 78
问题描述:
使用 Linux 和 C++,我想要一个执行以下操作的函数:
string f(string s)
{
string r = system("foo < s");
return r;
}
显然上面的方法行不通,但你明白我的意思了。我有一个字符串s
,我想将其作为应用程序“foo”的子进程执行的标准输入传递,然后我想将其标准输出记录到字符串中r
并返回它。
我应该使用哪种 Linux 系统调用或 POSIX 函数组合?我使用的是 Linux 3.0,不需要该解决方案来与旧系统配合使用。
解决方案 1:
eerpini 提供的代码无法按书写方式工作。例如,请注意,在父级中关闭的管道末端随后会被使用。请查看
close(wpipefd[1]);
随后写入该封闭描述符。这只是转置,但它表明此代码从未被使用过。下面是我测试过的版本。不幸的是,我改变了代码风格,因此这不被接受为 eerpini 代码的编辑。
唯一的结构变化是我只在子级中重定向 I/O(请注意,dup2 调用仅在子路径中)。这非常重要,因为否则父级的 I/O 会变得混乱。感谢 eerpini 提供的初始答案,我在开发这个答案时使用了它。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
int aStdinPipe[2];
int aStdoutPipe[2];
int nChild;
char nChar;
int nResult;
if (pipe(aStdinPipe) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(aStdoutPipe) < 0) {
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
nChild = fork();
if (0 == nChild) {
// child continues here
// redirect stdin
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
exit(errno);
}
// redirect stdout
if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
exit(errno);
}
// redirect stderr
if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// run child process image
// replace this with any exec* function find easier to use ("man exec")
nResult = execve(szCommand, aArguments, aEnvironment);
// if we get here at all, an error occurred, but we are in the child
// process, so just exit
exit(nResult);
} else if (nChild > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(aStdinPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// Include error check here
if (NULL != szMessage) {
write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
}
// Just a char by char read here, you can change it accordingly
while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// done with these in this example program, you would normally keep these
// open of course as long as you want to talk to the child
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
} else {
// failed to create child
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
}
return nChild;
}
解决方案 2:
由于您希望双向访问该进程,因此您必须使用管道明确执行 popen 在后台执行的操作。我不确定这些在 C++ 中是否会发生变化,但这是一个纯 C 示例:
void piped(char *str){
int wpipefd[2];
int rpipefd[2];
int defout, defin;
defout = dup(stdout);
defin = dup (stdin);
if(pipe(wpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(pipe(rpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(dup2(wpipefd[0], 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(rpipefd[1], 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(fork() == 0){
close(defout);
close(defin);
close(wpipefd[0]);
close(wpipefd[1]);
close(rpipefd[0]);
close(rpipefd[1]);
//Call exec here. Use the exec* family of functions according to your need
}
else{
if(dup2(defin, 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(defout, 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
close(defout);
close(defin);
close(wpipefd[1]);
close(rpipefd[0]);
//Include error check here
write(wpipefd[1], str, strlen(str));
//Just a char by char read here, you can change it accordingly
while(read(rpipefd[0], &ch, 1) != -1){
write(stdout, &ch, 1);
}
}
}
实际上你可以这样做:
创建管道并将 stdout 和 stdin 重定向到两个管道的末端(请注意,在 linux 中,pipe() 创建单向管道,因此您需要使用两个管道来实现您的目的)。
Exec 现在将启动一个新进程,该进程具有 stdin 和 stdout 管道的末端。
关闭未使用的描述符,将字符串写入管道,然后开始读取进程可能转储到另一个管道的内容。
dup() 用于在文件描述符表中创建重复条目。而 dup2() 则更改描述符指向的内容。
注意:正如 Ammo@ 在他的解决方案中提到的,我上面提供的或多或少是一个模板,如果您只是尝试执行代码它将不会运行,因为显然缺少一个 exec* (函数系列),所以子进程将在 fork() 之后几乎立即终止。
解决方案 3:
Ammo 的代码存在一些错误处理错误。子进程在 dup 失败后返回而不是退出。也许可以将子 dup 替换为:
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
)
{
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件