将 exec 输出重定向到缓冲区或文件

Posted

技术标签:

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

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

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

【问题讨论】:

【参考方案1】:

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

string cmd = "/bin/ls > " + filepath;

execl("/bin/sh", "sh", "-c", cmd.c_str(), 0);

【讨论】:

【参考方案2】:

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

选项 1:文件

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

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

选项 2:管道

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

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

【讨论】:

以上是关于将 exec 输出重定向到缓冲区或文件的主要内容,如果未能解决你的问题,请参考以下文章

将标准输出重定向到缓冲区,而不是使用 >

重定向输出遇到的缓冲问题

使用System.out.printf()输出日志重定向到文件后显示混乱问题

搞懂 fflush(stdout)

tee命令详解

五周第三次课(1月10日) 8.1 shell介绍 8.2 命令历史 8.3 命令补全和别名 8.4 通配符 8.5 输入输出重定向