tee
命令是一个常规的Unix程序,就像
sh
、
sort
或者
cat
一样。
在调用
tee
命令之前(创建将执行
tee
命令的进程的
fork
之后),处理处理
< existingInputFile
和
> newOutputFile2
的所有I/O重定向工作都是由shell完成的。该命令的标准输入来源于
existingInputFile
,标准输出连接到
newOutputFile2
。传递给
tee
的唯一参数是
argv[0]
(字符串
tee
)和
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()
函数来复制已经单独打开的文件描述符,使其成为标准输入:
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);
请注意,在这种情况下关闭
fd
非常重要。否则,命令将使用至少一个未在命令行中指定的额外文件描述符打开。对于标准输出重定向,您需要类似的代码块。
或者您可以使用:
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);
close(1);
char * o_filename = "newOutputFile2";
fd = open(o_filename, O_WRONLY|O_CREAT|O_TRUNC, 0644);
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
名称被发明之前,我已经使用八进制权限了许多年。
http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tee.c
中的函数tee_files()
。 - Henk Langeveld