我需要开始一个过程,让我们说foo
。我想看到stdout / stderr正常,但是grep
字符串bar
的stderr。一旦找到bar
应该杀死stderr foo
。
这可能吗?
答案 0 :(得分:5)
我最初写了一个方法来做这个涉及流变换,但它不是很好。一些评论与该版本有关。如果您好奇,请查看历史记录。
这是一种方法:
(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
的语法,并将其称为PIDFILE
PIDFILE
命名的文件,再次为hygeine foo
;这是在复合语句中完成的,因此&
绑定到foo
而不绑定到整个前面的管道foo
的标准错误重定向到流程替换,等待bar
出现,然后杀死foo
(稍后会更新)foo
的PID捕获到变量中,将其写入由PIDFILE
命名的文件,然后等待它,以便整个命令等待foo
退出之前自己退出;当发生这种情况时,|| true
会丢弃foo
的错误退出状态。进程替换中的代码如下:
tee
输入(foo
的标准错误),将tee
的标准输出重定向到标准错误,以便foo
的标准错误确实出现在标准错误grep -q
,查找指定的模式,并在找到它时(或当它到达流的末尾时)退出,而不打印任何内容之后(如果找到了字符串并成功退出),shell继续... kill
在PIDFILE
命名的文件中捕获PID的进程,即foo
答案 1 :(得分:5)
Tom Anderson’s answer非常好,但kill $(cat $PIDFILE)
只会在foo
自行终止或通过Ctrl-C终止时发生在我的系统上。以下解决方案适合我
while read g
do
if [[ $g =~ bar ]]
then
kill $!
fi
done < <(
exec foo 2> >(tee /dev/tty)
)
答案 2 :(得分:3)
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脚本也会退出。
答案 3 :(得分:2)
作为另一个答案的替代方法,一种方法是使用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
但这显然是针对特定的。
答案 4 :(得分:2)
我实际上设法找到了一种方法来做到这一点,没有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
解释这里使用的众多子壳:
A
的主体与正常的标准输出重复运行到fd 3.它运行子shell B
和F
,其中B
标准输出到了标准输入F
。
B
的正文与A
的管道在fd 4上重复运行。
C
运行你的实际foo命令,其stderr连接到从C
到D
的管道,并且它的标准输出从fd 3复制;也就是说,恢复到全局标准输出。然后它将foo
的PID写入fd 4;也就是说,子shell F
在其stdin上的管道。
D
运行tee
命令从管道接收其stderr上的任何foo
打印件。它将输出复制到/dev/fd/2
(以使其显示在全局stderr上)和连接到子shell E
的管道。
E
greps for bar然后,当找到时,将fin
写入fd 4,即写入F
在其stdin上的管道。请注意&&
,如果grep在未找到fin
的情况下遇到EOF,请确保不会写入bar
。
F
从C
读取PID fin
和来自E
的{{1}}终结符。如果正确输出fin
终止符,则会终止foo
。
编辑:修复了丢失的tee
以将foo的stderr复制到真正的stderr。