プロセスを開始する必要がありますfoo。stdout/stderr を通常どおりに表示したいのですがgrep、 string の stderrを表示したいと思いますbar。barstderr で見つかったら、 fookill する必要があります。
これは可能ですか?
私は当初、ストリームのスウィズリングを含むこれを行う方法を書きましたが、あまり良くありませんでした。一部のコメントはそのバージョンに関連しています。気になる方は歴史を調べてみてください。
これを行う方法は次のとおりです。
(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。fooます。これは、前のパイプライン全体ではなく&バインドするように、複合ステートメントで行われます。foofooプロセス置換にリダイレクトします(これについては後で説明します)。barfoofooの PID を変数に取り込み、それを という名前のファイルに書き込みPIDFILE、それを待ちます。これにより、コマンド全体fooが終了する前に終了するのを待ちます。これは、それが発生したとき|| trueのエラー終了ステータスを破棄しますfoo。プロセス置換内のコードは次のように機能します。
tee入力 (の標準エラー) は、の標準出力を標準エラーにfooリダイレクトするため、その標準エラーは実際に標準エラーに表示されます。teefoogrep -qに指定されたパターンを探す入力に対して実行され、それが見つかるとすぐに (またはストリームの最後に到達すると) 終了し、何も出力せずに終了します。文字列と正常に終了しました) シェルは続きます ...killによって名前が付けられたファイルに取り込まれた PID を持つプロセスPIDFILE、つまりfooトム・アンダーソンの答えは非常に良いですが、それは自分のシステムで終了するか、Ctrl-C を介して終了したkill $(cat $PIDFILE)場合にのみ発生します。foo次の解決策は私にとってはうまくいきます
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 スクリプトも終了します。
他の答えの代わりとして、1つの方法は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 互換シェルで動作するようにこれを行う方法を見つけることができました (私は試してみました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 に複製された通常の stdout で実行されます。サブシェルBを実行し、 の stdin にパイプされたFの stdout を使用します。BF
fd 4で複製されBたパイプを使用して実行される本体。A
Cstderr を to からのパイプに接続し、stdout を fd 3 から複製して、実際の foo コマンドを実行しCますD。つまり、グローバル stdout に復元されます。次に、PIDfooを fd 4 に書き込みます。つまり、サブシェルFが標準入力に持つパイプに。
Dstderr に出力されたteeものをパイプから受け取ってコマンドを実行します。fooその出力を/dev/fd/2(グローバル stderr に表示するため) と subshell に接続されたパイプの両方にコピーしますE。
Ebar を grep し、見つかった場合はfinfd 4、つまりFstdin にあるパイプに書き込みます。grep が見つからずに EOF に遭遇した場合は&&no が書き込まれていることを確認してください。finbar
Fから PID を読み取り、 からCターミネータfinを読み取りますE。ターミネータが適切に出力された場合fin、それは を殺しfooます。
編集tee: foo の stderr を実際の stderr にコピーするための欠落を修正しました。