tee 命令是一个常规的 Unix 程序,就像 sh 或 sort 或 cat。
处理< existingInputFile 和> newOutputFile2 所涉及的所有I/O 重定向工作都由shell 在调用tee 命令之前完成(在fork 创建将执行tee 的进程之后)命令)。该命令的标准输入来自existingInputFile,其标准输出为newOutputFile2。提供给tee 的唯一参数是argv[0](字符串tee)和argv[1](字符串newOutputFile),外加一个空指针来标记参数列表的结尾。
特别注意,existingInputFile的实际读取不涉及shell;它只是打开它进行读取并将其连接到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 或 2>&1 或类似的东西,否则您通常不会修改标准错误。
如果您愿意,可以将 0644 写为:
O_IRUSR|O_IWUSR|O_IRGRP|O_IROTH
(如果要使用组和其他写入权限(0666),请添加|O_IWGRP|O_IWOTH;无论如何,权限将由umask修改)。就个人而言,我发现八进制更容易阅读,但我在 O_Ixyyy 名称被发明之前几年就开始使用八进制权限。