如何控制popen stdin,stdout,stderrredirect?

我很困惑如何popen()redirect标准input,标准输出和stderr的subprocess在UNIX中。 关于popen()的手册页在这方面不是很清楚。 电话

FILE *p = popen("/usr/bin/foo", "w"); 

派生一个subprocess并执行一个带有参数“-c”,“/ usr / bin / foo”的shell,并将这个shell的stdin(redirect到foo的stdin),stdoutredirect到p。 但是,stderr会发生什么? 它背后的一般原则是什么?

我注意到,如果我在foo中打开一个文件(使用fopen,socket,accept等),并且父进程没有标准输出,则会为其分配下一个可用的文件编号,即1。 这提供了来自fprintf(stderr,…)等调用的意外结果。

这可以通过写作来避免

 FILE *p = popen("/usr/bin/foo 2>/dev/null", "w"); 

在父母程序中,但是他们有更好的方法吗?

popen(3)只是一个库函数,依靠fork(2)pipe(2)来完成实际工作。

但是pipe(2)只能创build单向pipe道。 要发送subprocessinput,并捕获输出,则需要打开两个pipe道。

如果你也想捕获stderr ,那么这是可能的,但是你需要三个pipe道和一个select循环来仲裁stdoutstderrstream之间的读取。

这里有一个双pipe版本的例子。

简单的想法:为什么不在命令string中加上“2>&1”来强制bash将stderrredirect到stdout(OK,写stdin仍然是不可能的,但至less我们得到stderr和stdout到我们的C程序中)。

popen()的返回值在所有方面都是标准的I / Ostream,除了它必须用pclose()而不是fclose(3)来closures。 写入这样的stream写入命令的标准input; 该命令的标准输出与调用popen()的进程的标准输出相同,除非这个命令本身被改变了。 相反,从“popened”stream中读取命令的标准输出,命令的标准input与调用popen()的过程相同。

从它的manpage中,它允许您读取标准输出的命令或写入其标准input。 它没有提到任何有关stderr的事情。 因此这不是redirect的。

如果你提供“w”,你会发送你的东西到被执行的shell的stdin。 因此,这样做

 FILE * file = popen("/bin/cat", "w"); fwrite("hello", 5, file); pclose(file); 

将使shell执行/ bin / cat,并将string"hello"作为其标准inputstream。 如果你想redirect,例如stderr到文件"foo" ,首先要做的是在执行上面的代码之前:

 FILE * error_file = fopen("foo", "w+"); if(error_file) { dup2(fileno(error_file), 2); fclose(error_file); } 

它将打开该文件,并将其文件描述符复制到2,然后closures原始文件描述符。

现在,如果你的父母closures了你的标准输出,那么如果孩子open它会得到1,因为这是(如果标准input已经打开)下一个免费的文件描述符。 我只看到解决scheme只是使用dup2和重复的东西到父母,如上面的代码。 请注意,如果小孩打开stdout ,则不会在父项中打开stdout 。 它在那里保持closures。

通过Bart Trojanowski检查popenRWE 。 干净的方式来做所有3pipe道。

如果你只是想获得STDERR,试试这个:

 #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <sys/wait.h> #include <malloc.h> #include <unistd.h> #include <string.h> #include <sys/types.h> /* * Pointer to array allocated at run-time. */ static pid_t *childpid = NULL; /* * From our open_max(), {Prog openmax}. */ static int maxfd; FILE * mypopen(const char *cmdstring, const char *type) { int i; int pfd[2]; pid_t pid; FILE *fp; /* only allow "r" "e" or "w" */ if ((type[0] != 'r' && type[0] != 'w' && type[0] != 'e') || type[1] != 0) { errno = EINVAL; /* required by POSIX */ return(NULL); } if (childpid == NULL) { /* first time through */ /* allocate zeroed out array for child pids */ maxfd = 256; if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL) return(NULL); } if (pipe(pfd) < 0) return(NULL); /* errno set by pipe() */ if ((pid = fork()) < 0) { return(NULL); /* errno set by fork() */ } else if (pid == 0) { /* child */ if (*type == 'e') { close(pfd[0]); if (pfd[1] != STDERR_FILENO) { dup2(pfd[1], STDERR_FILENO); close(pfd[1]); } } else if (*type == 'r') { close(pfd[0]); if (pfd[1] != STDOUT_FILENO) { dup2(pfd[1], STDOUT_FILENO); close(pfd[1]); } } else { close(pfd[1]); if (pfd[0] != STDIN_FILENO) { dup2(pfd[0], STDIN_FILENO); close(pfd[0]); } } /* close all descriptors in childpid[] */ for (i = 0; i < maxfd; i++) if (childpid[i] > 0) close(i); execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); _exit(127); } /* parent continues... */ if (*type == 'e') { close(pfd[1]); if ((fp = fdopen(pfd[0], "r")) == NULL) return(NULL); } else if (*type == 'r') { close(pfd[1]); if ((fp = fdopen(pfd[0], type)) == NULL) return(NULL); } else { close(pfd[0]); if ((fp = fdopen(pfd[1], type)) == NULL) return(NULL); } childpid[fileno(fp)] = pid; /* remember child pid for this fd */ return(fp); } int mypclose(FILE *fp) { int fd, stat; pid_t pid; if (childpid == NULL) { errno = EINVAL; return(-1); /* popen() has never been called */ } fd = fileno(fp); if ((pid = childpid[fd]) == 0) { errno = EINVAL; return(-1); /* fp wasn't opened by popen() */ } childpid[fd] = 0; if (fclose(fp) == EOF) return(-1); while (waitpid(pid, &stat, 0) < 0) if (errno != EINTR) return(-1); /* error other than EINTR from waitpid() */ return(stat); /* return child's termination status */ } int shellcmd(char *cmd){ FILE *fp; char buf[1024]; fp = mypopen(cmd,"e"); if (fp==NULL) return -1; while(fgets(buf,1024,fp)!=NULL) { printf("shellcmd:%s", buf); } pclose(fp); return 0; } int main() { shellcmd("ls kangear"); } 

你会得到这个:

 shellcmd:ls: cannot access kangear: No such file or directory