将 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
,所以你可以使用你正常的fscanf
、fread
等。
【讨论】:
【参考方案5】:fork 后,使用dup2(2)
将文件的 FD 复制到 stdout 的 FD 中,然后执行。
【讨论】:
以上是关于将 exec 输出重定向到缓冲区或文件的主要内容,如果未能解决你的问题,请参考以下文章
使用System.out.printf()输出日志重定向到文件后显示混乱问题
五周第三次课(1月10日) 8.1 shell介绍 8.2 命令历史 8.3 命令补全和别名 8.4 通配符 8.5 输入输出重定向