为什么`command 1>file.txt 2>file.txt` 的行为与`command 1>file.txt 2>&1` 的行为不同?

fhi*_*iyo 21 shell bash io-redirection echo

当您想将 stdout 和 stderr 重定向到同一个文件时,可以使用command 1>file.txt 2>&1, 或command &>file.txt。但是为什么command 1>file.txt 2>file.txt与上面两个命令的行为不同呢?

以下是验证命令。

$ cat redirect.sh
#!/bin/bash

{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file.txt 2>&1
{ echo -e "output\noutput" && echo -e "error" 1>&2; } 1>file1.txt 2>file1.txt
{ echo -e "error" 1>&2 && echo -e "output\noutput"; } 1>file2.txt 2>file2.txt
{ echo -e "output" && echo -e "error\nerror" 1>&2; } 1>file3.txt 2>file3.txt
{ echo -e "error\nerror" 1>&2 && echo -e "output"; } 1>file4.txt 2>file4.txt

$ ./redirect.sh

$ echo "---file.txt---"; cat file.txt;\
echo "---file1.txt---"; cat file1.txt; \
echo "---file2.txt---"; cat file2.txt; \
echo "---file3.txt---"; cat file3.txt; \
echo "---file4.txt----"; cat file4.txt;
 ---file.txt---
output
output
error
---file1.txt---
error

output
---file2.txt---
output
output
---file3.txt---
error
error
---file4.txt----
output
rror
Run Code Online (Sandbox Code Playgroud)

就结果来看,运行时第二个回显字符串似乎覆盖了第一个回显字符串command 1>file.txt 2>file.txt,但我不知道为什么会这样。(某处有参考吗?)

Jde*_*eBP 44

你需要知道两件事:

  • 进程的应用程序模式端已知的打开文件描述符引用称为文件描述的内部内核对象,它是打开文件的实例。每个文件可以有多个文件描述,并且多个文件描述符共享一个文件描述。
  • 当前文件位置是一个的属性文件描述。因此,如果多个文件描述符映射到单个文件描述,则它们都共享相同的当前文件位置,并且使用一个此类文件描述符对文件位置进行的更改会影响所有其他此类文件描述符。

    此类更改由调用read()/ readv()write()/ writev()lseek()等系统调用的进程执行。该echo命令当然会调用write()/ writev()

所以发生的事情是这样的:

  • command 1>file.txt 2>&1只创建一个文件描述,因为 shell 只打开一个文件一次。壳品牌二者的标准输出和标准误差文件描述符映射到单个文件说明。它将标准输出复制到标准错误上。因此,通过任一文件描述符写入将移动共享的当前文件位置:每次写入都在前一次写入公共文件描述之后。正如您所看到的,echo命令的结果不会相互覆盖。
  • command 1>file.txt 2>file.txt创建两个文件描述,因为 shell 两次打开同一个文件,以响应两个显式重定向。标准输出和标准错误文件描述符映射到两个不同的文件描述,然后又映射到同一个文件。这两个文件描述具有完全独立的当前文件位置,并且每次写入都紧跟上一次对同一文件描述的写入。正如您所看到的,结果是通过一个写入的内容可以覆盖通过另一个写入的内容,根据您执行写入的顺序以各种不同的方式。

进一步阅读


jes*_*e_b 16

使用>告诉它覆盖文件。由于您在两个不同的操作中将 stdout 和 stderr 写入文件,因此最后一个写入将覆盖第一个。

你可以做:

command 1>>file.txt 2>>file.txt

或者

command &>file.txt 仅 bash v4 及更高版本。

>> 告诉它附加文件,这样它就不会替换之前操作的输出。

&> 只是一种更简单的写作方式 2>&1

  • @jlmg:追加模式文件是可查找的,但每次写入都以隐含的查找作为前缀。我不太清楚隐含的搜索是否是原子的。 (4认同)
  • 为什么 `ls 1>&0` 和 `ls 0>&0` 仍然显示 ls 的输出? (2认同)