
commandA
次の印刷を実行したいと思います。
line1
line2
line3
started successfully
line4
...
...
その行が印刷されたら、started successfully
別のコマンドを開始したいと思います(そして出力を多重化して両方を端末に印刷することもできます)。
Bashでどのようにこれを達成できますか?
答え1
次のいずれかの方法でこれを実行できます。
$ outputfile=output.log
$ commandA | tee "$outputfile" & tail --pid=$! -n+1 -f "$outputfile" |grep -q "started successfully" && commandB; wait
説明する:
- この
tee
コマンドを使用してSTDOUTと$outputfile
。 - バックグラウンドでコマンドを実行すると、出力を追跡できるようになりました。
tail --pid=$! -n+1 -f "$outputfile"
tail
- コマンドを実行し$outputfile
、最初の行から出力を追跡します。- このフラグは、コマンドが完了したら終了するようにする
--pid=$!
ために必要です。tail
commandA
- このフラグは、コマンドが完了したら終了するようにする
grep -q "started successfully"
- 必要な文字列が最初に表示されると正常に(静かに)終了します。&& commandB
-grep
文字列が見つかると実行されますcommandB
。- 実行を保証する
&&
代わりに使用する必要があります。;
commandB
ただ の出力で文字列が見つかった場合commandA
。この文字列を書かずに完了した場合commandA
(たとえば、失敗した場合)、2番目のコマンドを実行したくありません。
- 実行を保証する
wait
- 以前に完了した場合は、commandB
シェルに戻る前にcommandA
実行が続くまで待ちます。commandA
- または、以前に完了した場合は、フォアグラウンドで実行を続行するのではなく
fg
実行できます。wait
commandA
commandB
- または、以前に完了した場合は、フォアグラウンドで実行を続行するのではなく
修正する
tail
@PaulPazderskiの答えからインスピレーションを得て、ログファイルやコマンドがない別の方法があります。
$ commandA | tee >(grep -q "started successfully" && commandB; cat >/dev/null)
唯一の欠点がcommandA
完了したら今後 commandB
、シェルは完了するとすぐにプロンプトに戻りcommandA
、commandB
バックグラウンドで実行され続け、端末に出力を書き込むこともできます。
この問題を解決するには、最終出力を別のコマンドにパイプするだけです。たとえば、cat
stdoutに何も記録されていない場合にのみ、シェルがプロンプトに戻るようにします(これはコマンド全体が完了したことを意味しますcommandB
)。
$ commandA | tee >(grep -q "started successfully" && commandB; cat >/dev/null) | cat
答え2
次のような「トラック出力」スクリプトを試してみましょう。
#!/bin/bash
while IFS= read -r line; do
echo "$line"
if [[ "$line" == "started successfully" ]]; then
do_something
# optional exit but that could disrupt commandA with a SIGPIPE
fi
done
そして電話してください
commandA | track-output
またはtee
置換を使用して処理し、スクリプトにエコーラインがない場合
commandA | tee >(track-output)
答え3
可能:
{
commandA 3<&- | perl -pe '
exec "commandB <&3 3<&-" if !$pid && /started successfully/ && ($pid = fork) == 0;
END {if ($pid) {wait; exit($? & 127 ? $? + 128 : $? >> 8)}}'
} 3<&0
bashとGNUの代わりにzshを使用している場合は、tee
同様のことができますが、commandB
終了ステータスが失われるという警告が表示されます。
{
commandA 3<&- |
tee -p >(grep -q 'started successfully' && commandB <&3 3<&-)
} 3<&0
これは待ち時間がないことを除いてbashでも機能します(常にフォークを最適化しないexec
前にcommandB
asを追加する必要があります)。bash
commandB
両方の方法と標準入力が妨げられないようにcommandA
注意してください。 stdout はソースからパイプに変更されます。これはパイプを見なければならない場合に必要ですが、その出力はまたはによって渡されます。標準出力は中断されません。commandB
commandA
started successfully
perl -p
tee
commandB
いくつかのコマンドに注意してくださいバッファーstdout が端末装置でない場合の出力です。したがって、commandA
ここの出力は次のようになります。管路started successfully
、大きなチャンクの一部としてのみ印刷され、commandB
できるだけ早く開始されないことがあります。
以下を使用してその出力を擬似端末に接続し始めるzsh
と、この問題を解決できます。zpty
commandA
zmodload zsh/zpty
# start commandA with only stdout going to the pseudo-terminal
# stdin and stderr restored.
zpty A 'command A <&3 3<&- 2>&4 4>&-' 3<&0 4>&2
pid=
while zpty -r A line; do
print -rn - $line
if (( ! pid )) && [[ $line = *"started successfully" ]]; then
{ commandB <&3 3<&- & } 3<&0
pid=$!
fi
done
(( ! pid )) || wait $pid
答え4
以下のコードは「Some text」を印刷したcommand2
後に実行されますcommand1
(一致するgrepが終了するため)。その後、出力command1
はcommand2
標準出力として印刷されます。
command1 | tee >(grep -o -m1 "Some text"; command2 )