SIGPIPE 由于文件描述符和进程替换
SIGPIPE due to file descriptors and process substitution
我正在尝试从执行工具的 bash 函数中保存一些日志(其中一些 运行 在子 shell 中)。此外,我想将所有错误打印到终端。
我的代码在按下 ctr-c 和一个奇怪的日志文件时导致一个 sigpipe 和退出代码 141。管道故障似乎是由于在陷阱内将 stdout 重定向到 stderr,这会破坏 tee 命令的 stdout 流。有趣的是,代码按预期终止,退出代码为 130,没有在陷阱或 cat
命令中使用重定向。
我仍然无法修复和解释生成的日志文件。为什么有些回声两次,为什么trap回声也写入文件?
为什么sigpipe不是由函数内的重定向引起的?
trap '
echo trap_stdout
echo trap_stderr >&2
' INT
fun(){
echo fun_stdout
echo fun_stderr >&2
( sleep 10 | cat )
}
echo > log
fun >> log 2> >(tee -a log)
日志文件
fun_stdout
fun_stderr
fun_stderr
trap_stdout
编辑:根据 oguz ismail answer
的工作示例
exec 3>> log
exec 4> >(tee -ai log >&2)
fun 2>&4 >&3
exec 3>&-
exec 4>&-
SIGPIPE 的来源是 SIGINT(由 ctrl/c 发起)被发送到所有进程 运行ning:“主”bash 进程(执行 'fun' 函数),子 shell 执行 'tee -a'。结果,在 Ctrl/C,两人都被杀了。当主进程试图发送 'trap_stderr' 到 te“tee”进程时,它得到 SIGPIPE,因为“tee”已经死了。
考虑到 'tee -a' 的作用,保护它免受 SIGINT 的影响并允许它 运行 直到 'fun' 完成(或被杀死)是有意义的。考虑对最后一行的以下更改
fun >> log 2> >(trap '' INT ; tee -a log >&2)
这将生成日志文件:
Console (stderr)
fun_stderr
^Ctrap_stderr
Log File: (no duplicates)
fun_stdout
fun_stderr
trap_stdout
trap_stderr
以上内容还将解决第二个问题,即日志文件中的重复行。这是使用 tee 将每个 stderr 行发送到日志文件和 stdout 的结果。鉴于 stdout 刚刚(通过“>>log”)重定向到 'log' 文件,输出的两个副本都被发送到日志文件,none 到终端
鉴于重定向是按顺序执行的,更改 'tee' 行以将输出发送到原始标准错误(而不是已经重定向的标准输出)将在终端(或任何标准错误)上显示输出
Why are there some echos twice
fun
的标准输出在其标准错误被重定向到为 tee
创建的 FIFO 之前被重定向到 log
,因此 tee
继承了一个被重定向到的标准输出log
。我可以这样证明:
$ : > file 2> >(date)
$ cat file
Sat Jul 25 18:46:31 +03 2020
更改重定向的顺序将解决这个问题。例如:
fun 2> >(tee -a log) >> log
and why are the trap echos written to the file as well?
如果在 shell 仍在执行 fun
时触发了为 SIGINT 设置的陷阱,则与 fun
关联的重定向生效是完全正常的。
要将陷阱操作的标准输出和标准错误连接到主要 shell 的标准输出和标准错误,您可以这样做:
exec 3>&1 4>&2
handler() {
: # handle SIGINT here
} 1>&3 2>&4
trap handler INT
或类似的东西;这个想法是复制主要 shell 的标准输出和标准错误。
Why isn't the sigpipe caused earlier by the redirection within the function?
因为tee
在执行echo fun_stderr >&2
时还活着。并且 sleep
不会向其标准输出写入任何内容,因此它无法触发 SIGPIPE。
此脚本因 SIGPIPE 而终止的原因是 tee
也接收到键盘生成的 SIGINT,并在执行与 SIGINT 关联的陷阱操作之前终止。结果,在执行 echo trap_stderr >&2
时,由于其 stderr 连接到刚才关闭的管道,shell 接收到 SIGPIPE。
为了避免这种情况,正如已经建议的那样,您可以使 tee
忽略 SIGINT。不过,您不需要为此设置空陷阱,-i
选项就足够了。
fun 2> >(tee -a -i log) >> log
我正在尝试从执行工具的 bash 函数中保存一些日志(其中一些 运行 在子 shell 中)。此外,我想将所有错误打印到终端。
我的代码在按下 ctr-c 和一个奇怪的日志文件时导致一个 sigpipe 和退出代码 141。管道故障似乎是由于在陷阱内将 stdout 重定向到 stderr,这会破坏 tee 命令的 stdout 流。有趣的是,代码按预期终止,退出代码为 130,没有在陷阱或 cat
命令中使用重定向。
我仍然无法修复和解释生成的日志文件。为什么有些回声两次,为什么trap回声也写入文件?
为什么sigpipe不是由函数内的重定向引起的?
trap '
echo trap_stdout
echo trap_stderr >&2
' INT
fun(){
echo fun_stdout
echo fun_stderr >&2
( sleep 10 | cat )
}
echo > log
fun >> log 2> >(tee -a log)
日志文件
fun_stdout
fun_stderr
fun_stderr
trap_stdout
编辑:根据 oguz ismail answer
的工作示例exec 3>> log
exec 4> >(tee -ai log >&2)
fun 2>&4 >&3
exec 3>&-
exec 4>&-
SIGPIPE 的来源是 SIGINT(由 ctrl/c 发起)被发送到所有进程 运行ning:“主”bash 进程(执行 'fun' 函数),子 shell 执行 'tee -a'。结果,在 Ctrl/C,两人都被杀了。当主进程试图发送 'trap_stderr' 到 te“tee”进程时,它得到 SIGPIPE,因为“tee”已经死了。
考虑到 'tee -a' 的作用,保护它免受 SIGINT 的影响并允许它 运行 直到 'fun' 完成(或被杀死)是有意义的。考虑对最后一行的以下更改
fun >> log 2> >(trap '' INT ; tee -a log >&2)
这将生成日志文件:
Console (stderr)
fun_stderr
^Ctrap_stderr
Log File: (no duplicates)
fun_stdout
fun_stderr
trap_stdout
trap_stderr
以上内容还将解决第二个问题,即日志文件中的重复行。这是使用 tee 将每个 stderr 行发送到日志文件和 stdout 的结果。鉴于 stdout 刚刚(通过“>>log”)重定向到 'log' 文件,输出的两个副本都被发送到日志文件,none 到终端
鉴于重定向是按顺序执行的,更改 'tee' 行以将输出发送到原始标准错误(而不是已经重定向的标准输出)将在终端(或任何标准错误)上显示输出
Why are there some echos twice
fun
的标准输出在其标准错误被重定向到为 tee
创建的 FIFO 之前被重定向到 log
,因此 tee
继承了一个被重定向到的标准输出log
。我可以这样证明:
$ : > file 2> >(date)
$ cat file
Sat Jul 25 18:46:31 +03 2020
更改重定向的顺序将解决这个问题。例如:
fun 2> >(tee -a log) >> log
and why are the trap echos written to the file as well?
如果在 shell 仍在执行 fun
时触发了为 SIGINT 设置的陷阱,则与 fun
关联的重定向生效是完全正常的。
要将陷阱操作的标准输出和标准错误连接到主要 shell 的标准输出和标准错误,您可以这样做:
exec 3>&1 4>&2
handler() {
: # handle SIGINT here
} 1>&3 2>&4
trap handler INT
或类似的东西;这个想法是复制主要 shell 的标准输出和标准错误。
Why isn't the sigpipe caused earlier by the redirection within the function?
因为tee
在执行echo fun_stderr >&2
时还活着。并且 sleep
不会向其标准输出写入任何内容,因此它无法触发 SIGPIPE。
此脚本因 SIGPIPE 而终止的原因是 tee
也接收到键盘生成的 SIGINT,并在执行与 SIGINT 关联的陷阱操作之前终止。结果,在执行 echo trap_stderr >&2
时,由于其 stderr 连接到刚才关闭的管道,shell 接收到 SIGPIPE。
为了避免这种情况,正如已经建议的那样,您可以使 tee
忽略 SIGINT。不过,您不需要为此设置空陷阱,-i
选项就足够了。
fun 2> >(tee -a -i log) >> log