目标是制作一个简单的非侵入式包装器,将stdin和stdout跟踪到stderr:
#!/bin/bash
tee /dev/stderr | ./script.sh | tee /dev/stderr
exit ${PIPESTATUS[1]}
Run Code Online (Sandbox Code Playgroud)
测试脚本script.sh:
#!/bin/bash
echo asd
sleep 1
exit 4
Run Code Online (Sandbox Code Playgroud)
但是当脚本退出时,它不会终止包装器.可能的解决方案是tee从管道的第二个命令结束第一个:
#!/bin/bash
# Second subshell will get the PID of the first one through the pipe.
# It will be able to kill the whole script by killing the first subshell.
# Create a temporary named pipe (it's safe, conflicts will throw an error).
pipe=$(mktemp -u)
if ! mkfifo $pipe; then
echo "ERROR: debug tracing pipe creation failed." >&2
exit 1
fi
# Attach it to file descriptor 3.
exec 3<>$pipe
# Unlink the named pipe.
rm $pipe
(echo $BASHPID >&3; tee /dev/stderr) | (./script.sh; r=$?; kill $(head -n1 <&3); exit $r) | tee /dev/stderr
exit ${PIPESTATUS[1]}
Run Code Online (Sandbox Code Playgroud)
这是很多代码.还有另外一种方法吗?
Ewa*_*lor 30
我认为你正在寻找pipefail选项.从bash手册页:
pipefail
如果设置,则管道的返回值是以非零状态退出的最后(最右侧)命令的值,如果管道中的所有命令都成功退出,则返回零.默认情况下禁用此选项.
所以如果你用你的包装脚本开始
#!/bin/bash
set -e
set -o pipefail
Run Code Online (Sandbox Code Playgroud)
然后,当发生任何错误(set -e)时,包装器将退出,并将以您想要的方式设置管道的状态.
此处的主要问题显然是管道。在bash中,执行以下形式的命令时
command1 | command2
Run Code Online (Sandbox Code Playgroud)
和command2管芯或终止,其接收输出(管/dev/stdout)从command1变为断开。但是,折断的管道不会终止command1。仅当它尝试写入断开的管道时,才会发生这种情况,并在管道上退出sigpipe。在这个问题中可以简单地证明这一点。
如果要避免此问题,则应使用进程替代而不是管道,并将其重写为:
command2 < <(command1)
Run Code Online (Sandbox Code Playgroud)
对于OP,将变为:
./script.sh < <(tee /dev/stderr) | tee /dev/stderr
Run Code Online (Sandbox Code Playgroud)
也可以写成:
./script.sh < <(tee /dev/stderr) > >(tee /dev/stderr)
Run Code Online (Sandbox Code Playgroud)