首页 文章

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

提问于
浏览
27

我很困惑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 回答

  • 7

    popen(3) 只是一个库函数,它依赖于 fork(2)pipe(2) 来完成实际工作 .

    但是 pipe(2) 只能创建单向管道 . 要发送子进程输入,并捕获输出,您需要打开两个管道 .

    如果你想捕获 stderr ,'s possible, but then you'll需要 three 管道, select 循环来仲裁 stdoutstderr 流之间的读取 .

    双管道版本有一个示例here .

  • 5

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

  • 5

    popen()的返回值在所有方面都是普通的标准I / O流,除非必须用pclose()而不是fclose(3)来关闭它 . 写入这样的流写入命令的标准输入;命令的标准输出与调用popen()的进程相同,除非命令本身改变了它 . 相反,从“popened”流读取读取命令的标准输出,命令的标准输入与调用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 ,它也不会在父项中打开 stdout . 它在那里保持关闭 .

  • 31

    查看Bart Trojanowski的popenRWE . 干净的方式做所有3管道 .

  • 33

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

相关问题