很多 Linux 书籍都写到“重定向在命令之前执行”。让我们考虑一下:
$ cat bar
hello
$ ecno "foo" > bar
bash: ecno: command not found
$cat bar
- 如果 bar 不存在,则重定向会创建它。
- 如果 bar 存在并且命令不起作用,它会删除 bar 的内容。
但是它不会将任何输出复制到 bar 中(因为没有输出),因此应该说“重定向在命令之前部分执行”,因为 '>' 的一部分在这里不起作用,即将命令的输出复制到文件 bar 中。对吗?
答案1
来自 bash 手册第 3.6.2 节:
...如果确实存在的话截断为零大小。
重定向> file
总是会截断文件执行命令之前,这意味着无论输出如何,内容都会被删除;实际的写作如果有实际输出(最明显的是缺少write()
调用strace -f -e open,dup2,write bash -c 'true > out2.txt'
),并且没有发生进一步的错误,则会将文件存入文件。
因此,一切都按规范正常工作,措辞也正确。事实上,这种行为是由POSIX Shell 命令语言规范并得到所有 POSIX 兼容 shell 的支持,包括ksh
和dash
(又称 Ubuntu 的/bin/sh
,请参阅相关问题)。
在系统级别,重定向由dup 系列系统调用,这就是为什么重定向正式被称为重复文件描述符最突出的是当我们执行>/dev/null 2&>1
重定向类型。
可以strace
在以下成功和失败的命令示例中观察到命令。请注意如何使用标志打开文件O_CREAT|O_TRUNC
。
$ strace -f -e open,dup2,write bash -c 'nocommand > out.txt;echo "foobar" > out2.txt'
...
strace: Process 9633 attached
[pid 9633] open("out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
[pid 9633] dup2(3, 1) = 1
...
...
[pid 9633] write(2, "bash: nocommand: command not fou"..., 35bash: nocommand: command not found
) = 35
[pid 9633] +++ exited with 127 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=9633, si_uid=1000, si_status=127, si_utime=0, si_stime=0} ---
open("out2.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
dup2(3, 1) = 1
write(1, "foobar\n", 7) = 7
dup2(10, 1) = 1
+++ exited with 0 +++
答案2
嗯,第二部分成功了,因为它复制了空的输出到文件中。
例如,如果你使用 stderr2>
而不是 stdout 重定向,你会看到错误进入该文件:
$ ecno "foo" 2> bar
$
并且“bar”的内容更新为:
$ cat bar
-bash: ecno: command not found
文件被截断的事实也是重定向工作的一部分。
我希望这能帮助您理解重定向的工作原理!