代码之家  ›  专栏  ›  技术社区  ›  Dong Hoon

如何控制popen stdin、stdout、stderr重定向?

  •  28
  • Dong Hoon  · 技术社区  · 16 年前

    我对popen()如何重定向unix中子进程的stdin、stdout和stderr感到困惑。popen()上的手册在这方面不太清楚。电话

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

    分叉子进程并使用参数“-c”、“/usr/bin/foo”执行shell,并将此shell的stdin(这是foo的重定向stdin)和stdout重定向到p。但stderr会发生什么?它背后的一般原则是什么?

    我注意到,如果我在foo中打开一个文件(使用fopen、socket、accept等),并且父进程没有stdout,那么它将被分配下一个可用的文件号,即1,依此类推。这会从诸如fprintf(stderr,…)之类的调用中传递意外的结果。

    写作可以避免

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

    在父程序中,但他们的更好方法是什么?

    5 回复  |  直到 11 年前
        1
  •  33
  •   Alnitak    16 年前

    popen(3) 只是一个库函数,它依赖于 fork(2) pipe(2) 做真正的工作。

    然而 管道(2) 只能创建单向管道。要发送子进程输入并捕获输出,需要打开两个管道。

    如果你想捕捉 stderr 也有可能,但是你需要 管子和A select 循环来仲裁 stdout 标准错误 溪流。

    有一个例子 here 对于双管版本。

        2
  •  33
  •   martin    14 年前

    简单的想法:为什么不在命令字符串中添加“2>&1”,以强制bash将stderr重定向到stdout(好的,写入stdin仍然是不可能的,但至少我们将stderr和stdout导入到C程序中)。

        3
  •  7
  •   Nathan Campos    12 年前

    popen()的返回值是一个普通的标准I/O流。 尊重保存它必须用pclose()关闭,而不是 FHOCT(3)。写入这样的流,写入 命令;命令的标准输出与 调用popen()的进程,除非命令更改了该进程 本身。相反,从“弹出”流读取 命令的标准输出,命令的标准输入是 与调用popen()的进程相同。

    从它的手册页,所以它允许您读取命令标准输出或写入其标准输入。关于stderr,它什么都没说。因此,这是不重定向的。

    如果您提供“w”,您将把您的东西发送到执行的shell的stdin。这样,做

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

    将使shell执行/bin/cat,并将字符串传递给它 "hello" 作为其标准输入流。如果要重定向,例如stderr到文件 "foo" 在执行上述代码之前,请先执行以下操作:

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

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

    现在,如果您的stdout在您的父节点中关闭,那么如果子节点调用 open 它将得到1,因为这是(如果stdin已经打开)下一个可用的文件描述符。我看到的唯一解决方案是使用dup2并将某些内容复制到父级中,如上面的代码。注意,如果孩子打开 stdout 它会 制作 标准输出 在父级中也打开。它在那里保持关闭。

        4
  •  5
  •   kainjow    11 年前

    退房 popenRWE 巴特·特罗扬诺夫斯基。清洁所有3个管道。

        5
  •  5
  •   kangear    11 年前

    如果您只想获取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