POSIX'tee'命令如何工作? [英] How does the POSIX 'tee' command work?
问题描述
tee newOutputFile < existingInputFile > newOutputFile2
tee
到底将如何接受参数?会是这样吗?
- Tee将首先处理
newOutputFile < existingInputFile
因此,existentInputFile的内容将被写入newOutputFile -
newOutputFile > newOutputFile2
因此,newOutputFile的内容将被写入newOutputFile 2
我正在尝试编写一个处理该特定命令的外壳.但是,对于将参数传递给tee
的顺序,我感到困惑.我对程序进行编码的方式将会实现
tee newOutputFile2 < existingInputFIle
tee
命令是常规的Unix程序,就像sh
或sort
或cat
一样.
处理< existingInputFile
和> newOutputFile2
涉及的所有I/O重定向工作都是在调用tee
命令之前(由fork
创建创建将执行tee
的进程的)之后,由外壳程序完成的.命令).调用该命令的标准输入来自existingInputFile
,其标准输出进入newOutputFile2
.赋予tee
的唯一参数是argv[0]
(字符串tee
)和argv[1]
(字符串newOutputFile
),以及用于标记参数列表结尾的空指针.
请特别注意,existingInputFile
的实际读取中不涉及该外壳程序;它只是打开它进行读取并将其连接到tee
的标准输入,但是不知道tee
命令是否实际读取了它.类似地,shell并不参与实际写入newOutputFile2
的过程.它只是打开并截断它(或创建它)并将其连接到tee
的标准输出,但是不知道tee
命令是否实际上对其写入了任何内容.在这种情况下,运行tee
命令时,父外壳完全是被动的,不执行任何I/O.
根据设计,tee
读取其标准输入,并将所有内容的一个副本写入其参数列表中给定的每个文件,再将一个副本写入标准输出.
给我的印象是外壳参与了文件的实际读写.因此,当我调用
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).除非命令行中包含2>/dev/null
或2>&1
之类的I/O重定向,否则通常不会修改标准错误.
如果愿意,可以将0644写为:
O_IRUSR|O_IWUSR|O_IRGRP|O_IROTH
(如果要使用组和其他写许可权,则添加|O_IWGRP|O_IWOTH
(0666);无论如何,该许可权都将由umask
修改).就个人而言,我发现八进制很容易阅读,但是在发明O_Ixyyy
名称的几年之前,我开始使用八进制权限.
tee newOutputFile < existingInputFile > newOutputFile2
How exactly will tee
take in the arguments? Would it be like this?
- Tee will first process
newOutputFile < existingInputFile
So the contents of existingInputFile will be written into newOutputFile newOutputFile > newOutputFile2
So the contents of newOutputFile will be written into newOutputFile 2
I am trying to write a shell that process this particular command. However, I am confused as to which order to pass in the arguments to tee
. The way I have coded my program up it will do
tee newOutputFile2 < existingInputFIle
The tee
command is a regular Unix program, just like sh
or sort
or cat
.
All the I/O redirection work involved in handling < existingInputFile
and > newOutputFile2
is done by the shell before the tee
command is invoked (after the fork
that creates the process that will execute the tee
command). The command is invoked with its standard input coming from existingInputFile
and with its standard output going to newOutputFile2
. The only arguments given to tee
are argv[0]
(the string tee
) and argv[1]
(the string newOutputFile
), plus a null pointer to mark the end of the argument list.
Note specifically that the shell is not involved in the actual reading of existingInputFile
; it just opens it for reading and connects it to the standard input of tee
, but has no knowledge of whether the tee
command actually reads it or not. Similarly, the shell is not involved in the actual writing to newOutputFile2
; it just opens and truncates it (or creates it) and connects it to the standard output of tee
, but has no knowledge of whether the tee
command actually writes anything to it. In this context, while the tee
command is running, the parent shell is completely passive, doing no I/O.
By design, tee
reads its standard input and write one copy of everything to each of the files given in its argument list and one more copy to standard output.
I was under the impression that the shell was involved in the actual reading and writing of the files. So when I call
execvp
, it only takes in the command (in this casetee
) and the final file to write the contents to (in this casenewOutputFile2
). I am trying to create my own shell program, how would I do the I/O redirection. Is this wheredup2
comes into play?
The shell is only involved in opening and closing, but not in the reading and writing, of the files. In your command line tee newOutputFile < existingInputFile > newOutputFile2
, the command is tee
and the only other argument is newOutputFile
. In general, the command (tee
in this case) has no knowledge of the name of the file that is providing it with standard input, nor of the name of the file that it is writing to on its standard output. Indeed, especially with tee
, the input is most often a pipe rather than a file, and very often the output is also a pipe rather than a file:
some_command arg1 arg2 | tee some_command.log | another_command its_arg1 its_arg2 > output.file
In your own shell program, you could use dup2()
to duplicate the file descriptor you'd opened separately so that it becomes standard input:
// 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!
Note that it is important to close fd
in this scenario. Otherwise, the command is run with at least one extra file descriptor open that was not specified in the command line. You'd have a similar block of code for the standard output redirection.
Or you could use:
// 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);
This is because open()
returns the lowest available previously not open file descriptor, so by closing 0, you know that open()
will return 0 on success and -1 on failure (even if 0 was previously closed). Then, by induction, you know that after closing 1, open()
will return 1 on success and -1 on failure (even if 1 was previously closed). You don't normally tinker with standard error unless the command line includes I/O redirection such as 2>/dev/null
or 2>&1
or something similar.
If you prefer, you can write 0644 as:
O_IRUSR|O_IWUSR|O_IRGRP|O_IROTH
(and add |O_IWGRP|O_IWOTH
if you want to go with group and other write permission (0666); the permissions will be modified by the umask
anyway). Personally, I find the octal a lot easier to read, but I started using octal permissions a number of years before the O_Ixyyy
names were invented.
这篇关于POSIX'tee'命令如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!