Bash

為什麼 command 1>file.txt 2>file.txt 的行為與 command 1>file.txt 2>&1 不同?

  • January 2, 2018

如果要將 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

就結果而言,看起來第二個回顯字元串在您執行時會覆蓋第一個回顯字元串command 1>file.txt 2>file.txt,但我不知道為什麼會這樣。(在某處有參考嗎?)

你需要知道兩件事:

  • 程序的應用程序模式端已知的打開文件描述符引用稱為文件描述的內部核心對象,它是打開文件的實例。每個文件可以有多個文件描述,並且多個文件描述符共享一個文件描述。
  • 目前文件位置是**文件描述的一個屬性。因此,如果多個文件描述符映射到一個文件描述,它們都共享相同的目前文件位置,並且使用一個這樣的文件描述符對文件位置的更改會影響所有其他這樣的文件描述符。此類更改由呼叫read()/ readv()write()/ writev()lseek()和類似系統呼叫的程序執行。該echo命令呼叫write()/writev()當然。

所以會發生這樣的事情:

  • command 1>file.txt 2>&1只創建一個文件描述,因為 shell 只打開一個文件一次。Shell 使標準輸出和標準錯誤文件描述符都映射到該單個文件描述。它將標準輸出複製到標準錯誤上。因此,通過任一文件描述符的寫入將移動共享的目前文件位置:每次寫入都在前一次寫入公共文件描述之後。正如您所看到的,echo命令的結果不會相互覆蓋。
  • command 1>file.txt 2>file.txt創建兩個文件描述,因為 shell 打開同一個文件兩次,以響應兩個顯式重定向。標準輸出和標準錯誤文件描述符映射到兩個不同的文件描述,然後又映射到同一個文件。這兩個文件描述具有完全獨立的目前文件位置,並且每次寫入都會立即執行同一文件描述上的前一次寫入。正如您所看到的,結果是通過一個寫入的內容可以覆蓋通過另一個寫入的內容,根據您執行寫入的順序以各種不同的方式。

進一步閱讀

引用自:https://unix.stackexchange.com/questions/391456