重定向标准输出和错误时,追加工作无需 >>

重定向标准输出和错误时,追加工作无需 >>

我有一个文件test.txt,但没有名为的文件test。当我尝试

ls test test.txt > new 2>new

由于未使用,我预计new会被覆盖。>>但在输出文件中我附加了这两个内容。为什么会这样呢?

答案1

长话短说 bash在写入任何内容之前打开并截断所有涉及的文件。stdoutstderr都被发送到,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会执行以下操作(按顺序):

  1. 作为标准输出打开new,必要时创建/截断文件。
  2. 作为标准错误打开new,必要时创建/截断文件。
  3. 运行ls:这会将内容写入new.

如您所见,bash截断所有涉及的文件开始ls.这意味着当使用 运行某些内容时... >new 2>newnew基本上会被截断“两次”,并且然后,输出被重定向到它。您期望的行为需要独立bash捕获ls的 stdout 和 stderr ,并在写入之前一个接一个地打开它们。基本上:

  1. 开始ls
  2. 当出现某些情况时stdout,打开new、截断它并写入。
  3. 当出现某些情况时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,可以看到:fcntldup2

  • 首先,文件new被打开,并分配给文件描述符3
  • 然后文件描述符1被复制到文件描述符10
  • 然后文件描述符3(即 file new)被复制到文件描述符1(即10现在)。

> new以上所有内容均在您的命令中提及。然后发生相同的序列,但对于文件描述符,请在命令中2引用。2>new

之后,你就有了两个文件描述符10,并且11都指向文件new,这些描述符变成了stderrand stdoutof ls。运行时ls,它写入stderrstdout,它们指向同一文件new,并且部分输出被截断,因为stdout已覆盖stderr

相关内容