我需要开始一个过程,让我们说foo。我希望看到 stdout/stderr 正常,但grepstderr for string bar。一旦bar在 stderr 中发现foo应该被杀死。
这可能吗?
我最初编写了一种涉及流混合的方法,但它不是很好。一些评论与该版本有关。如果您好奇,请查看历史。
这是一种方法:
(PIDFILE=$(mktemp /tmp/foo.XXXXXX) && trap "rm $PIDFILE" 0 \
&& { foo \
2> >(tee >(grep -q bar && kill $(cat $PIDFILE)) >&2) \
& PID=$! && echo $PID >$PIDFILE ; wait $PID || true; })
老式的噩梦燃料。这里发生了什么事?
mktemp并将其命名为PIDFILEPIDFILE,再次用于 hygeinefoo;这是在复合语句中完成的,因此&绑定到foo而不是绑定到整个前面的管道foo的标准错误重定向到等待bar出现然后终止的进程替换foo(稍后会更多)foo到一个变量中,将其写入以 命名的文件中PIDFILE,然后等待它,这样整个命令foo在自己退出之前等待退出;丢弃发生这种情况时的|| true错误退出状态foo。进程替换中的代码如下工作:
tee输入(foo的标准错误),将tee的标准输出重定向到标准错误,这样foo的标准错误确实出现在标准错误上grep -q在输入上运行,它查找指定的模式,并在找到它时立即退出(或者当它到达流的末尾时),不打印任何东西,之后(如果它找到字符串并成功退出)外壳继续...kill在名为的文件中捕获PID的进程PIDFILE,即foo汤姆安德森的回答非常好,但kill $(cat $PIDFILE)只有在我的系统foo上自行终止或通过 Ctrl-C 终止时才会发生。以下解决方案对我有用
while read g
do
if [[ $g =~ bar ]]
then
kill $!
fi
done < <(
exec foo 2> >(tee /dev/tty)
)
Expect 旨在根据流程的输出采取行动。最简单的解决方案是让 Expect 启动进程,然后在看到预期输出时退出。例如:
expect -c 'set msg {Saw "foo" on stderr. Exiting process.}
spawn /bin/bash -c "echo foo >&2; sleep 10"
expect "foo" { puts $msg; exit }'
如果生成的进程正常结束(例如,在看到“foo”之前),那么 Expect 脚本也会退出。
作为另一个答案的替代方案,一种方法是使用 bash 的coproc工具:
{coproc FOO { foo; } 2>&1 1>&3; } 3>&1
CHILD=$!
while read line <&${FOO[0]}; do
if echo "$line" | grep -q bar; then
kill $CHILD
else
echo "$line"
fi
done
不过,这显然是特定于 bash 的。
实际上,我设法找到了一种无需 PID 文件或协同例程的方法,并且可以在所有与 POSIX 兼容的 shell 中工作(我已经尝试过bash)dash。至少在支持的系统上/dev/fd/,但这应该是几乎所有的系统。
不过,这有点令人费解,所以我不确定它是否符合您的喜好。
( # A
( # B
( /tmp/foo 2>&1 1>&3 & echo $! >&4 ) | # C
( tee /dev/fd/2 | ( grep -q bar && echo fin >&4 ) ) # D and E
) 4>&1 | ( # F
read CHILD
read STATUS
if [ "$STATUS" = fin ]; then
kill $CHILD
fi
)
) 3>&1
解释这里使用的众多子shell:
使用复制到 fd 3 的正常标准输出运行的主体A。它运行子shell B,F并将标准输出通过B管道传输到F.
在 fd 4 上重复B的管道运行的主体。A
C运行您的实际 foo 命令,其 stderr 连接到来自Cto的管道D,其标准输出从 fd 3 复制;即恢复到全局标准输出。然后将 PID 写入foofd 4;也就是说,到 subshellF在其标准输入上的管道。
D运行一个tee命令,从管道接收foo其 stderr 上打印的任何内容。它将输出复制到/dev/fd/2(为了让它显示在全局标准错误上)和连接到 subshell 的管道E。
Egrep 查找 bar,然后在找到时写入finfd 4,即写入F其标准输入的管道。请注意,如果 grep 遇到 EOF 而没有找到,请&&确保写入 no 。finbar
F,然后,从 中读取 PID 并从 中读取C终止fin符E。如果fin终止符被正确输出,它会杀死foo.
编辑:修复了tee将 foo 的 stderr 复制到真正的 stderr 的缺失。