Linux 3.0:使用pipe道stdin / stdout执行subprocess
在Linux 3.0 / C ++下:
我想要一个function,执行以下操作:
string f(string s) { string r = system("foo < s"); return r; }
上述显然不起作用,但你明白了。 我有一个strings,我想通过作为应用程序“foo”的subprocess执行的标准input,然后我想将其标准输出logging到stringr,然后返回它。
我应该使用什么linux系统调用或posix函数的组合?
由eerpini提供的代码不能像写入一样工作。 请注意,例如,之后使用在父项中closures的pipe道末端。 看着
close(wpipefd[1]);
并随后写入该已closures的描述符。 这只是换位,但是它显示这个代码从来没有被使用过。 以下是我testing过的一个版本。 不幸的是,我改变了代码风格,所以这不被接受作为编辑eerpini的代码。
唯一的结构变化是我只redirect子项中的I / O(注意dup2调用只在子path中)。这是非常重要的,因为否则父项I / O会被搞乱。 感谢eerpini提供的初步答案,我在开发这个答案时使用了这个答案。
#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; }
既然你想双向访问进程,你将不得不使用pipe道明确地在后台做什么。 我不确定这些在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); } } }
有效地,你这样做:
- 创buildpipe道并将stdout和stdinredirect到两个pipe道的末端(请注意,在linux中,pipe()会创build单向pipe道,因此您需要使用两个pipe道来满足您的需求)。
- Exec现在将开始一个新的进程,其中包含stdin和stdout的pipe道的末端。
- closures未使用的描述符,将string写入pipe道,然后开始读取任何进程可能转储到其他pipe道的内容。
dup()用于在文件描述符表中创build一个重复的条目。 而dup2()更改描述符指向的内容。
注意:正如Ammo @在他的解决scheme中提到的那样,我上面提供的或多或less是一个模板,如果你只是试图执行代码,它就不会运行,因为明显存在exec *(函数族)子(fork)后几乎立即终止。
弹药的代码有一些error handling的错误。 subprocess在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]);