我有一个文件test.txt
,但没有名为的文件test
。当我尝试
ls test test.txt > new 2>new
由于未使用,我预计new
会被覆盖。>>
但在输出文件中我附加了这两个内容。为什么会这样呢?
答案1
长话短说 bash
在写入任何内容之前打开并截断所有涉及的文件。stdout
和stderr
都被发送到,new
因为bash
在开始打印时已经截断了文件(两次)ls
。
这就是bash
准备/处理 I/O 重定向的方式。当您请求将命令重定向 ( >
) 到文件时,bash
基本上会打开该文件,并在必要时创建它。如果文件已存在,则会被截断。在您的情况下,这是通过open
系统调用和一些标志来完成的:
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
O_CREAT
如果文件不存在则创建该文件,而O_TRUNC
如果文件存在则截断它。该系统调用是重定向初始化open
的一部分,这意味着当您使用多个重定向操作时,例如...bash
$ ls test test.txt > new 2>new
...bash
首先打开所有相关文件。因此,在运行之前ls
,它会new
使用相同的标志打开两次:
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666)
这意味着,基本上,在运行命令时,bash
会执行以下操作(按顺序):
- 作为标准输出打开
new
,必要时创建/截断文件。 - 作为标准错误打开
new
,必要时创建/截断文件。 - 运行
ls
:这会将内容写入new
.
如您所见,bash
截断所有涉及的文件前开始ls
.这意味着当使用 运行某些内容时... >new 2>new
,new
基本上会被截断“两次”,并且然后,输出被重定向到它。您期望的行为需要独立bash
捕获ls
的 stdout 和 stderr ,并在写入之前一个接一个地打开它们。基本上:
- 开始
ls
。 - 当出现某些情况时
stdout
,打开new
、截断它并写入。 - 当出现某些情况时
stderr
,再次打开new
,截断它,然后写入。
然而,消息可能会交织在一起:重定向的程序很可能会向 写入一些内容stdout
,然后向 写入其他内容stderr
,然后返回stdout
...管理所有这些内容将是可怕的(这可能会导致不良(未定义?)行为......)。
答案2
您不会附加这两个内容。你会得到一个奇怪的输出:
$ ls testasdasd qtsingleapp-homecu-bcbf-3e8 >new 2>new
$ cat new
qtsingleapp-homecu-bcbf-3e8
: No such file or directory
如果您有这两个内容,您应该会看到:
$ ls testasdasd qtsingleapp-homecu-bcbf-3e8 >new 2>&1
$ cat new
ls: cannot access testasdasd: No such file or directory
qtsingleapp-homecu-bcbf-3e8
那么让我们来strace
看看发生了什么:
$ strace -f -e trace=open,close,write,fcntl,dup2 sh -c 'ls testasdsad qtsingleapp-homecu-bcbf-3e8 > new 2>new'
open("/etc/ld.so.cache", O_RDONLY) = 3
close(3) = 0
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY) = 3
close(3) = 0
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_DUPFD, 10) = 10
close(1) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
open("new", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(2, F_DUPFD, 10) = 11
close(2) = 0
fcntl(11, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 2) = 2
close(3) = 0
Process 7523 attached
....
[pid 7523] write(2, "ls: ", 4) = 4
[pid 7523] write(2, "cannot access testasdsad", 24) = 24
[pid 7523] open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 7523] open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
[pid 7523] write(2, ": No such file or directory", 27) = 27
[pid 7523] write(2, "\n", 1) = 1
[pid 7523] write(1, "qtsingleapp-homecu-bcbf-3e8\n", 28) = 28
[pid 7523] close(1) = 0
[pid 7523] close(2) = 0
Process 7523 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
dup2(10, 1) = 1
close(10) = 0
dup2(11, 2) = 2
close(11) = 0
查看,的序列open
,可以看到:fcntl
dup2
- 首先,文件
new
被打开,并分配给文件描述符3
- 然后文件描述符
1
被复制到文件描述符10
- 然后文件描述符
3
(即 filenew
)被复制到文件描述符1
(即10
现在)。
> new
以上所有内容均在您的命令中提及。然后发生相同的序列,但对于文件描述符,请在命令中2
引用。2>new
之后,你就有了两个文件描述符10
,并且11
都指向文件new,这些描述符变成了stderr
and stdout
of ls
。运行时ls
,它写入stderr
和stdout
,它们指向同一文件new
,并且部分输出被截断,因为stdout
已覆盖stderr
。