The tee
命令是一个常规的 Unix 程序,就像sh
or sort
or cat
.
处理涉及的所有 I/O 重定向工作< existingInputFile
and > newOutputFile2
是由 shell 之前完成的tee
命令被调用(之后fork
创建将执行的进程tee
命令)。该命令是通过其标准输入来调用的existingInputFile
其标准输出为newOutputFile2
。给出的唯一论据是tee
are argv[0]
(字符串tee
) and argv[1]
(字符串newOutputFile
),加上一个空指针来标记参数列表的末尾。
特别注意,shell不参与实际读取existingInputFile
;它只是打开它进行读取并将其连接到标准输入tee
,但不知道是否tee
命令实际上是否读取它。同样,shell 也不参与实际写入newOutputFile2
;它只是打开并截断它(或创建它)并将其连接到标准输出tee
,但不知道是否tee
命令实际上向其中写入任何内容。在此背景下,虽然tee
命令运行时,父 shell 完全是被动的,不执行任何 I/O。
通过设计,tee
读取其标准输入,并将所有内容的一份副本写入其参数列表中给出的每个文件,并将另一份副本写入标准输出。
我的印象是 shell 参与了文件的实际读写。所以当我打电话时execvp
,它只接受命令(在本例中tee
)以及将内容写入的最终文件(在本例中newOutputFile2
)。我正在尝试创建自己的 shell 程序,我将如何进行 I/O 重定向。这是哪里吗dup2
发挥作用吗?
shell只参与文件的打开和关闭,不参与文件的读写。在你的命令行中tee newOutputFile < existingInputFile > newOutputFile2
,命令是tee
唯一的另一个论点是newOutputFile
。一般来说,命令(tee
在这种情况下)不知道为其提供标准输入的文件的名称,也不知道它在标准输出上写入的文件的名称。确实,尤其是与tee
,输入通常是管道而不是文件,并且输出通常也是管道而不是文件:
some_command arg1 arg2 | tee some_command.log | another_command its_arg1 its_arg2 > output.file
在你自己的 shell 程序中,你可以使用dup2()
复制您单独打开的文件描述符,使其成为标准输入:
// Redirect standard input from existingInputFile using dup2()
char *i_filename = "existingInputFile";
int fd = open(i_filename, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "unable to open file %s for reading (%d: %s)\n",
i_filename, errno, strerror(errno));
exit(1);
}
dup2(fd, STDIN_FILENO);
close(fd); // Crucial!
请注意,关闭很重要fd
在这种情况下。否则,该命令将在至少打开一个未在命令行中指定的额外文件描述符的情况下运行。您将有一个用于标准输出重定向的类似代码块。
或者你可以使用:
// Redirect standard input from existingInputFile
close(0);
char *i_filename = "existingInputFile";
int fd = open(i_filename, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "unable to open file %s for reading (%d: %s)\n",
i_filename, errno, strerror(errno));
exit(1);
}
assert(fd == 0);
// Redirect standard output to NewOutputFile2
close(1);
char * o_filename = "newOutputFile2";
fd = open(o_filename, O_WRONLY|O_CREAT|O_TRUNC, 0644); // Classically 0666
if (fd < 0)
{
fprintf(stderr, "unable to open file %s for writing (%d: %s)\n",
o_filename, errno, strerror(errno));
exit(1);
}
assert(fd == 1);
这是因为open()
返回先前未打开的最低可用文件描述符,因此通过关闭 0,您知道open()
成功时返回 0,失败时返回 -1(即使 0 之前已关闭)。然后,通过归纳,你知道关闭 1 后,open()
成功时返回 1,失败时返回 -1(即使 1 之前已关闭)。您通常不会修改标准错误,除非命令行包含 I/O 重定向,例如2>/dev/null
or 2>&1
或类似的东西。
如果您愿意,可以将 0644 写为:
O_IRUSR|O_IWUSR|O_IRGRP|O_IROTH
(并添加|O_IWGRP|O_IWOTH
如果您想使用组和其他写入权限(0666);权限将被修改umask
反正)。就我个人而言,我发现八进制更容易阅读,但我在几年前就开始使用八进制权限O_Ixyyy
名字被发明了。