Bash

Bash:“tee”被下一個管道中的“no-op”阻止

  • October 8, 2022
  • 當通過管道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)。

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