Bash:“tee”被下一個管道中的“no-op”阻止
- 當通過管道
tee
將其標準輸出發送到 no-op (:
) 命令時,不會列印任何內容並且文件大小為零。- 當通過管道
tee
將其標準輸出發送到 acat
時,所有內容都會正確列印並且文件大小大於零。這是一個顯示它的程式碼範例(以腳本的第一個輸入參數為條件):
#!/usr/bin/env bash log_filepath="./log.txt" [ -f "$log_filepath" ] && { rm "$log_filepath" || exit 1 ; } fail_tee="$1" while IFS= read -r -d $'\n' line ; do printf "%s%s\n" "prefix: " "$line" | \ tee -a "$log_filepath" | \ { if [ -n "$fail_tee" ]; then # Nothing is printed to stdout/terminal # $log_filepath size is ZERO. : # do nothing. else # Each line in the input is prefixed w/ "prefix: " and sent to stdout # $log_filepath size is 46 bytes cat fi } done <<'EOF' 1 23 456 7890 EOF
不勝感激背後的解釋。
我對 no-op
:
命令的期望是它不應該阻止tee
將輸出發送到文件。
:
in仍然是一個程序,tee ... | :
它持有由 shell 設置的管道的讀取端,其另一端正在tee
寫入。只是:
立即退出,這會阻止它從管道中讀取。通常的行為是,當管道的讀取器退出(讀取端文件描述符關閉)時,寫入器在下一次寫入時收到 SIGPIPE 信號,這會導致它退出。因為,
tee
在 POSIX 規範中似乎沒有任何例外,它只說:如果寫入任何成功打開的文件操作數失敗,則寫入其他成功打開的文件操作數和標準輸出應繼續,但退出狀態應為非零。否則,將應用實用程序描述預設值中指定的預設操作。
雖然老實說我在“實用程序描述預設值”部分找不到合適的部分,但通常的行為就是我上面提到的。
這通常是您想要的,因為這意味著如果管道的右側退出,左側也會退出,並且不會無用地繼續執行可能很長的任務。或者(更糟糕的是)無助地試圖寫入一個不承認任何寫入的阻塞管道,因為數據無處可去。
GNU coreutils 版本
tee
具有-p
和--output-error
選項來控制寫入失敗時的操作:未指定時的預設操作
--output-error
是在寫入管道出錯時立即退出,並診斷寫入非管道輸出的錯誤。並且預設使用
-p
的是warn-nopipe
模式,它被描述為“診斷寫入任何輸出而不是管道的錯誤”,而不是使其退出的其他選項。因此,至少使用 GNU 版本,您可以使用
tee -p ... | ...
它來防止它在管道閱讀器退出時退出。或者,您可以將右側程序安排為模仿黑洞的東西,例如cat > /dev/null
(它仍然讀取和寫入它獲得的所有內容,但核心最終會忽略寫入的數據/dev/null
)。