問題描述
SIGPIPE 由於文件描述符和進程替換 (SIGPIPE due to file descriptors and process substitution)
我正在嘗試從執行工具的 bash 函數中保存一些日誌(其中一些在子 shell 中運行)。此外,我想將所有錯誤打印到終端。
我的代碼在點擊 ctr‑c 和一個奇怪的日誌文件時會導致一個 sigpipe 和退出代碼 141。管道失敗似乎是由陷阱內的 stdout 重定向到 stderr 引起的,這會破壞 tee 命令的 stdout 流。有趣的是,代碼按預期終止,退出代碼為 130,沒有在陷阱或 cat
命令中使用重定向。
我仍然無法修復和解釋生成的日誌文件。為什麼會有兩次迴聲,為什麼陷阱迴聲也會寫入文件?
為什麼沒有
參考解法
方法 1:
The source of the SIGPIPE is that the SIGINT (initiated by ctrl/c) is sent to ALL processes running: both the "main" bash process (executing the 'fun' function), and the sub shell executing the 'tee ‑a'. As a result, on Ctrl/C, both get killed. When the main process tries to send 'trap_stderr' to te "tee" process, it get SIGPIPE, because the "tee" has already died.
Given the role of the 'tee ‑a', it make sense to protect it from the SIGINT, and allow it to run until 'fun' complete (or killed). Consider the following change to the last line
fun >> log 2> >(trap '' INT ; tee ‑a log >&2)
which will produce the log file:
Console (stderr) fun_stderr ^Ctrap_stderr Log File: (no duplicates) fun_stdout fun_stderr trap_stdout trap_stderr
The above will also address the second question is about duplicate lines in the log file. This is the result of using tee to send each stderr line to log file AND stdout. Given that stdout just got redirect (by the '>>log') to the 'log' file, both copy of the output are sent to the log file, and none to the terminal
Given the redirection are performed sequentially, changing the 'tee' line to sent the output to the original stderr (instead of the already redirect stdout) will show the output on the terminal (or whatever stderr is)
方法 2:
Why are there some echos twice
</blockquote>fun
's stdout is redirected tolog
before its stderr is redirected to the FIFO created fortee
, thustee
inherits a stdout that is redirected tolog
. I can prove that like so:$ : > file 2> >(date) $ cat file Sat Jul 25 18:46:31 +03 2020
Changing the order of redirections will fix that. E.g.:
fun 2> >(tee ‑a log) >> log
and why are the trap echos written to the file as well?
If the trap set for SIGINT is triggered while the shell is still executing
fun
, its perfectly normal that the redirections associated withfun
takes effect.To connect the trap action's stdout and stderr to those of the main shell, you can do:
exec 3>&1 4>&2 handler() { : # handle SIGINT here } 1>&3 2>&4 trap handler INT
Or something alike; the idea is making copies of the main shell's stdout and stderr.
Why isn't the sigpipe caused earlier by the redirection within the function?
Because
tee
is alive whileecho fun_stderr >&2
is being executed. Andsleep
does not write anything to its stdout, so it can not trigger a SIGPIPE.The reason why this script terminates due to a SIGPIPE is that
tee
receives the SIGINT generated by the keyboard as well and terminates before the trap action associated with SIGINT is executed. As a result, while executingecho trap_stderr >&2
, since its stderr is connected to a pipe that has been closed moments ago, the shell receives the SIGPIPE.To avoid this, as already suggested, you can make
tee
ignore SIGINT. You don't need to set an empty trap for that though, the‑i
option is enough.fun 2> >(tee ‑a ‑i log) >> log
(by pyr0、dash‑o、oguz ismail)
參考文件