終了していない以前のコマンドが特定の文字列を印刷した後にコマンドを実行するには?

終了していない以前のコマンドが特定の文字列を印刷した後にコマンドを実行するには?

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

説明する:

  1. このteeコマンドを使用してSTDOUTと$outputfile
  2. バックグラウンドでコマンドを実行すると、出力を追跡できるようになりました。
  3. tail --pid=$! -n+1 -f "$outputfile"tail- コマンドを実行し$outputfile、最初の行から出力を追跡します。
    • このフラグは、コマンドが完了したら終了するようにする--pid=$!ために必要です。tailcommandA
  4. grep -q "started successfully"- 必要な文字列が最初に表示されると正常に(静かに)終了します。
  5. && commandB-grep文字列が見つかると実行されますcommandB
    • 実行を保証する&&代わりに使用する必要があります。;commandBただ の出力で文字列が見つかった場合commandA。この文字列を書かずに完了した場合commandA(たとえば、失敗した場合)、2番目のコマンドを実行したくありません。
  6. wait- 以前に完了した場合は、commandBシェルに戻る前に commandA実行が続くまで待ちます。commandA
    • または、以前に完了した場合は、フォアグラウンドで実行を続行するのではなくfg実行できます。waitcommandAcommandB

修正する

tail@PaulPazderskiの答えからインスピレーションを得て、ログファイルやコマンドがない別の方法があります。

$ commandA | tee >(grep -q "started successfully" && commandB; cat >/dev/null)

唯一の欠点がcommandA完了したら今後 commandB、シェルは完了するとすぐにプロンプ​​トに戻りcommandAcommandBバックグラウンドで実行され続け、端末に出力を書き込むこともできます。

この問題を解決するには、最終出力を別のコマンドにパイプするだけです。たとえば、catstdoutに何も記録されていない場合にのみ、シェルがプロンプトに戻るようにします(これはコマンド全体が完了したことを意味します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前にcommandBasを追加する必要があります)。bashcommandB

両方の方法と標準入力が妨げられないようにcommandA注意してください。 stdout はソースからパイプに変更されます。これはパイプを見なければならない場合に必要ですが、その出力はまたはによって渡されます。標準出力は中断されません。commandBcommandAstarted successfullyperl -pteecommandB

いくつかのコマンドに注意してくださいバッファーstdout が端末装置でない場合の出力です。したがって、commandAここの出力は次のようになります。管路started successfully、大きなチャンクの一部としてのみ印刷され、commandBできるだけ早く開始されないことがあります。

以下を使用してその出力を擬似端末に接続し始めるzshと、この問題を解決できます。zptycommandA

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が終了するため)。その後、出力command1command2標準出力として印刷されます。

command1 | tee >(grep -o -m1 "Some text"; command2 )

関連情報