[英]Can a file descriptor be duplicated multiple times?
我一直在寻找很长一段时间,找不到我的问题的答案。
我正在尝试通过完全重定向在 C 中重现 shell。 为了做到这一点,我想在执行命令之前打开文件。
例如,在ls > file1 > file2
中,我使用dup2(file1_fd, 1)
和dup2(file2_fd, 1)
然后执行ls
来填充文件,但似乎标准的 output 只能打开一次,所以只有file2
会被填充,因为它是最后一个被复制的。
有没有办法将标准 output 重定向到多个文件?
有什么我想念的吗? 谢谢!
有没有办法将标准 output 重定向到多个文件?
许多文件描述符不能成为一个文件描述符。 您需要分别写入每个文件描述符。 这就是tee
实用程序为您所做的。
您所要求的是tee
命令存在的确切原因(您可以在此处查看其源代码)。
您不能多次使用dup2()
复制文件描述符。 正如您已经看到的,最后一个会覆盖任何先前的重复。 因此,您不能直接使用dup2()
将程序的 output 重定向到多个文件。
为此,您确实需要多个描述符,因此您必须打开两个文件,使用popen()
启动命令,然后从 pipe 读取并写入这两个文件。
这是一个非常简单的示例,说明如何做到这一点:
#include <stdio.h>
#include <stdlib.h>
#define N 4096
int main(int argc, const char *argv[]) {
FILE *fp1, *fp2, *pipe;
fp1 = fopen("out1.txt", "w");
if (fp1 == NULL) {
perror("fopen out1 failed");
return 1;
}
fp2 = fopen("out2.txt", "w");
if (fp2 == NULL) {
perror("fopen out2 failed");
return 1;
}
// Run `ls -l` just as an example.
pipe = popen("ls -l", "r");
if (pipe == NULL) {
perror("popen failed");
return 1;
}
size_t nread, nwrote;
char buf[N];
while ((nread = fread(buf, 1, N, pipe))) {
nwrote = 0;
while (nwrote < nread)
nwrote += fwrite(buf + nwrote, 1, nread - nwrote, fp1);
nwrote = 0;
while (nwrote < nread)
nwrote += fwrite(buf + nwrote, 1, nread - nwrote, fp2);
}
pclose(pipe);
fclose(fp2);
fclose(fp1);
return 0;
}
上面的代码只是粗略估计整个事情是如何工作的,它不会检查fread
、 fwrite
等的一些错误:你当然应该检查最终程序中的错误。
也很容易看出如何扩展它以支持任意数量的 output 文件(仅使用FILE *
数组)。
标准 output 与任何其他打开的文件没有什么不同,唯一的特殊特征是它是文件描述符1
(因此您的进程中只能有一个索引为 1 的文件描述符)您可以dup(2)
文件描述符1
来获取,假设文件描述符6
。 这是dup()
的任务,只是为了获取另一个文件描述符(具有不同的编号)而不是您用作源的文件描述符,但对于相同的 source 。 重复的描述符允许您使用任何重复的描述符对 output 漠不关心,或者更改打开的标志,如关闭 exec标志或非块或append标志(并非所有都是共享的,我不确定哪些可以更改而不影响其他人在重复)。 它们共享文件指针,因此您尝试对任何文件描述符的每个write()
都将在其他文件描述符中更新。
但重定向的想法并非如此。 unix 中的一个约定说,每个程序都会收到三个已经从其父进程打开的描述符。 所以要使用分叉,首先你需要考虑如何编写符号来表示一个程序将接收(已经打开)多个加入流。 在这里,问题更加复杂,因为您需要表达如何将数据流合并为一个,这使得合并问题与问题相关。
File dup()
ping 不是一种让文件描述符写入两个文件的方法……但反过来,它是一种让两个不同的文件描述符引用同一个文件的方法。
做你想做的事情的唯一方法是在你要使用的每个文件描述符上重复write(2)
调用。
正如一些答案所评论的那样, tee(1)
命令允许您在 pipe 中分叉数据流,但不能使用文件描述符, tee(1)
只是打开一个文件,然后write(2)
将所有输入放在除了 write(2) 也将其写入标准输出。
shell 中没有规定分叉数据流,因为没有规定在输入时加入(并行)数据流。 我认为这是 Steve Bourne 在 shell 设计中的一些废弃想法,您可能会达到同样的观点。
顺便说一句,只需研究使用通用dup2()
运算符的可能性,即 < n >&
m >,但再次考虑,对于重定向程序, 2>&3 2>&4 2>&5 2>&6
意味着你已预先打开 7 个文件描述符, 0
... 6
其中stderr
是描述符3
到6
的别名(因此写入任何这些描述符的任何数据都将出现在stderr
中)或者您可以使用2<file_a 3<file_b 4<file_c
意味着您的程序将使用从file_a
重定向的文件描述符2
(stderr) 执行,并且文件描述符3
和4
已经从文件file_b
和file_c
打开。 可能应该设计一些符号(现在我并不容易想到,如何 devise 它)以允许在已启动的不同进程之间进行管道(使用pipe(2)
系统调用)以执行某些任务,但您需要构建一个通用图以实现通用性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.