【问题标题】:Redirecting exec output to a buffer or file将 exec 输出重定向到缓冲区或文件
【发布时间】:2011-02-06 00:02:51
【问题描述】:

我正在编写一个 C 程序,其中我有 fork()exec()wait()。我想将我执行的程序的输出写入文件或缓冲区。

例如,如果我执行ls,我想将file1 file2 etc 写入缓冲区/文件。我认为没有办法读取标准输出,这是否意味着我必须使用管道?这里有没有我找不到的通用程序?

【问题讨论】:

    标签: c exec fork


    【解决方案1】:

    您也可以使用 linux sh 命令并传递一个包含重定向的命令:

    string cmd = "/bin/ls > " + filepath;
    
    execl("/bin/sh", "sh", "-c", cmd.c_str(), 0);
    

    【讨论】:

      【解决方案2】:

      你需要明确地决定你想做什么——最好解释得更清楚一点。

      选项 1:文件

      如果您知道要执行命令的输出到哪个文件,那么:

      1. 确保父母和孩子就名字达成一致(父母在分叉之前决定名字)。
      2. 父分叉 - 您有两个进程。
      3. 子级重新组织事物,以便文件描述符 1(标准输出)进入文件。
      4. 通常,您可以不考虑标准误差;你可以从 /dev/null 重定向标准输入。
      5. 然后子执行相关命令;所述命令运行并且任何标准输出进入文件(这是基本的 shell I/O 重定向)。
      6. 执行的进程然后终止。
      7. 同时,父进程可以采用以下两种主要策略之一:
        • 打开文件进行读取,并继续读取直到到达 EOF。然后它需要仔细检查孩子是否死了(这样就不会再有数据要读取),或者等待孩子的更多输入。
        • 等孩子死了再打开文件阅读。
        • 第一个的好处是父母可以在孩子跑步的同时做一些工作;第二种方法的优点是您不必玩弄 I/O 系统(反复读取 EOF)。

      选项 2:管道

      如果您希望父级读取子级的输出,请安排子级将其输出通过管道传回父级。

      1. 使用 popen() 可以轻松完成此操作。它将运行该进程并将输出发送到您的父进程。请注意,在子进程生成输出时,父进程必须处于活动状态,因为管道的缓冲区大小很小(通常为 4-5 KB),如果子进程生成的数据多于父进程未读取的数据,则子进程将阻塞,直到家长阅读。如果父母正在等待孩子死去,那么您就会陷入僵局。
      2. 使用 pipe() 等很难做到这一点。父调用 pipe(),然后分叉。子进程对管道进行排序,以便管道的写入端是其标准输出,并确保与管道相关的所有其他文件描述符都已关闭。这很可能使用 dup2() 系统调用。然后它执行所需的进程,将其标准输出发送到管道中。
      3. 同时,父级也会关闭不需要的管道末端,然后开始读取。当它在管道上获得 EOF 时,它知道孩子已经完成并关闭了管道;它也可以关闭管道的末端。

      【讨论】:

        【解决方案3】:

        用于将输出发送到另一个文件(我省略了错误检查以专注于重要细节):

        if (fork() == 0)
        {
            // child
            int fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        
            dup2(fd, 1);   // make stdout go to file
            dup2(fd, 2);   // make stderr go to file - you may choose to not do this
                           // or perhaps send stderr to another file
        
            close(fd);     // fd no longer needed - the dup'ed handles are sufficient
        
            exec(...);
        }
        

        用于将输出发送到管道,以便您可以将输出读入缓冲区:

        int pipefd[2];
        pipe(pipefd);
        
        if (fork() == 0)
        {
            close(pipefd[0]);    // close reading end in the child
        
            dup2(pipefd[1], 1);  // send stdout to the pipe
            dup2(pipefd[1], 2);  // send stderr to the pipe
        
            close(pipefd[1]);    // this descriptor is no longer needed
        
            exec(...);
        }
        else
        {
            // parent
        
            char buffer[1024];
        
            close(pipefd[1]);  // close the write end of the pipe in the parent
        
            while (read(pipefd[0], buffer, sizeof(buffer)) != 0)
            {
            }
        }
        

        【讨论】:

        • 你写:“close(pipefd[1]); //不再需要这个描述符”。为什么?
        • 在复制文件描述符并将标准输出发送到文件的行上,“1”表示什么?我找不到这方面的任何文档。
        • @MattBrzezinski - 1 是标准输出的文件描述符。
        • 如果您可以扩展您的答案并解释如何分别读取 stdout 和 stderr ,即如何从两个管道读取,那就太好了。我想这将涉及使用select,最后在孩子身上使用waitpid 来移除僵尸?
        • bottomupcs.com/file_descriptors.xhtml(通过谷歌搜索标准文件描述符编号
        【解决方案4】:

        因为您看起来要在 linux/cygwin 环境中使用它,所以您想使用 popen。这就像打开一个文件,只有你会得到执行程序stdout,所以你可以使用你正常的fscanffread等。

        【讨论】:

          【解决方案5】:

          fork 后,使用dup2(2) 将文件的 FD 复制到 stdout 的 FD 中,然后执行。

          【讨论】:

            猜你喜欢
            • 2015-11-10
            • 2014-02-18
            • 1970-01-01
            • 2013-10-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多