popen同时读写

是否可以读取和写入popen返回的文件描述符。 我有一个我想通过C控制的互动过程。如果用popen这是不可能的,有什么办法吗?

正如已经回答的那样,popen在一个方向上工作。 如果需要读写,可以使用pipe()创建一个管道,通过fork()和exec函数跨越一个新进程,然后使用dup2()重定向其输入和输出。 无论如何我更喜欢exec而不是popen,因为它可以让你更好地控制这个过程(例如你知道它的pid)

编辑:

正如评论所建议的那样,管道只能在一个方向上使用。 因此,您必须创建单独的管道进行读写。 由于之前发布的示例是错误的,我删除了它并创建了一个新的,正确的:

#include #include #include #include #include #include #include int main(int argc, char** argv) { pid_t pid = 0; int inpipefd[2]; int outpipefd[2]; char buf[256]; char msg[256]; int status; pipe(inpipefd); pipe(outpipefd); pid = fork(); if (pid == 0) { // Child dup2(outpipefd[0], STDIN_FILENO); dup2(inpipefd[1], STDOUT_FILENO); dup2(inpipefd[1], STDERR_FILENO); //ask kernel to deliver SIGTERM in case the parent dies prctl(PR_SET_PDEATHSIG, SIGTERM); //replace tee with your process execl("/usr/bin/tee", "tee", (char*) NULL); // Nothing below this line should be executed by child process. If so, // it means that the execl function wasn't successfull, so lets exit: exit(1); } // The code below will be executed only by parent. You can write and read // from the child using pipefd descriptors, and you can send signals to // the process using its pid by kill() function. If the child process will // exit unexpectedly, the parent process will obtain SIGCHLD signal that // can be handled (eg you can respawn the child process). //close unused pipe ends close(outpipefd[0]); close(inpipefd[1]); // Now, you can write to outpipefd[1] and read from inpipefd[0] : while(1) { printf("Enter message to send\n"); scanf("%s", msg); if(strcmp(msg, "exit") == 0) break; write(outpipefd[1], msg, strlen(msg)); read(inpipefd[0], buf, 256); printf("Received answer: %s\n", buf); } kill(pid, SIGKILL); //send SIGKILL signal to the child process waitpid(pid, &status, 0); } 

popen()和朋友不提供双向通信的原因是由于子popen()中的缓冲,它将容易死锁。 答案中讨论的所有临时管道和socketpair()解决方案都存在同样的问题。

在UNIX下,大多数命令都不可信任读取一行并立即处理并打印它,除非它们的标准输出是tty。 原因是默认情况下stdio缓冲用户空间中的输出,并推迟write()系统调用,直到缓冲区已满或stdio流关闭(通常是因为程序或脚本在输入时看到EOF后即将退出) 。 如果你通过管道写入这样一个程序的stdin,现在等待该程序的stdout的答案(不关闭入口管道),答案就会停留在stdio缓冲区中, 永远不会出现 – 这是一个死锁。

你可以通过使用伪tty与它们交谈来欺骗一些面向行的程序(例如grep )而不是缓冲; 看看libexpect(3) 。 但在一般情况下,您必须为每条消息重新运行不同的子进程,允许使用EOF来指示每条消息的结束并导致命令(或命令管道)中的任何缓冲区被刷新。 表现明显不是好事。

在perlipc手册页中查看有关此问题的更多信息(它适用于Perl中的双向管道,但无论主程序使用何种语言,缓冲注意事项都适用)。

你想要一些通常被称为popen2的东西。 这是一个没有错误检查的基本实现 。

popen()只能在读或写模式下打开管道,而不能同时打开。 看看这个线程的解决方法。

使用forkpty (它是非标准的,但API非常好,如果你没有它,你总是可以放入自己的实现)并exec你想要在子进程中与之通信的程序。

或者,如果tty语义不符合您的喜好,您可以编写类似forkpty东西,但是使用两个管道,一个用于每个通信方向,或者使用socketpair通过unix套接字与外部程序通信。

你不能使用popen来使用双向管道。

实际上,有些操作系统不支持双向管道,在这种情况下,套接字对( socketpair )是唯一的方法。

在netresolve后端之一中,我正在与脚本交谈,因此我需要写入stdin并从其stdout读取。 以下函数执行一个命令,其中stdin和stdout重定向到管道。 您可以使用它并根据自己的喜好进行调整。

 static bool start_subprocess(char *const command[], int *pid, int *infd, int *outfd) { int p1[2], p2[2]; if (!pid || !infd || !outfd) return false; if (pipe(p1) == -1) goto err_pipe1; if (pipe(p2) == -1) goto err_pipe2; if ((*pid = fork()) == -1) goto err_fork; if (*pid) { /* Parent process. */ *infd = p1[1]; *outfd = p2[0]; close(p1[0]); close(p2[1]); return true; } else { /* Child process. */ dup2(p1[0], 0); dup2(p2[1], 1); close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); execvp(*command, command); /* Error occured. */ fprintf(stderr, "error running %s: %s", *command, strerror(errno)); abort(); } err_fork: close(p2[1]); close(p2[0]); err_pipe2: close(p1[1]); close(p1[0]); err_pipe1: return false; } 

https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46

(我在Can popen()中使用相同的代码制作双向管道,如pipe()+ fork()? )

popen在两个方向上为我工作(读和写)我一直在两个方向使用popen()管道..

使用popen返回的文件描述符读取和编写子进程stdinstdout (命令,“w”)

它似乎工作正常..

我以为它会在我知道更好之前起作用,而且确实如此。 根据上面的post,这应该不起作用..这让我有点担心。

gcc on raspbian(raspbery pi debian)