web-dev-qa-db-ja.com

bashスクリプトで、行ごとにstdoutをキャプチャする方法

Bashスクリプトで、長いコマンドの標準出力を1行ずつキャプチャして、最初のコマンドがまだ実行されている間にそれらを分析して報告できるようにしたいと思います。これは私が想像できる複雑な方法です。

# Start long command in a separated process and redirect stdout to temp file
longcommand > /tmp/tmp$$.out &

#loop until process completes
ps cax | grep longcommand > /dev/null
while [ $? -eq 0 ]
do
    #capture the last lines in temp file and determine if there is new content to analyse
    tail /tmp/tmp$$.out

    # ...

    sleep 1 s  # sleep in order not to clog cpu

    ps cax | grep longcommand > /dev/null
done

もっと簡単な方法があるかどうか知りたいのですが。

編集:

私の質問を明確にするために、これを追加します。 longcommandは、1秒ごとに1行ずつステータスを表示します。 longcommandcompletesの前に出力をキャッチしたいと思います。

このようにして、期待する結果が得られない場合、longcommandを強制終了できます。

私が試してみました:

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

ただし、whatever(例:echo)は、longcommandが完了した後にのみ実行されます。

29
gfrigon

コマンドをwhileループにパイプするだけです。これにはいくつかのニュアンスがありますが、基本的に(bashまたは任意のPOSIXシェル):

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

これに関するもう1つの主な問題(以下のIFSのもの以外)は、ループが終了した後、ループ内から変数を使用しようとするときです。これは、ループが実際には変数にアクセスできないサブシェル(別のシェルプロセス)で実行されるためです(ループが実行すると終了し、その時点で変数は完全になくなります。これを回避するには、できるよ:

longcommand | {
  while IFS= read -r line
  do
    whatever "$line"
    lastline="$line"
  done

  # This won't work without the braces.
  echo "The last line was: $lastline"
}

lastpipebashを設定するHaukeの例は、別のソリューションです。

更新

コマンドの出力を「発生時に」処理していることを確認するには、stdbufを使用してプロセスを設定できますstdoutをラインバッファリングします。

stdbuf -oL longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

これにより、内部で出力をブロックにバッファリングする代わりに、パイプに一度に1行を書き込むようにプロセスが構成されます。プログラムがこの設定自体を内部で変更できることに注意してください。 unbufferexpectの一部)またはscriptでも同様の効果を得ることができます。

stdbufはGNUおよびFreeBSDシステムで使用できます。これはstdioバッファリングにのみ影響し、非setuid、非-動的にリンクされる-setgidアプリケーション(LD_PRELOADトリックを使用するため)。

36
Graeme
#! /bin/bash
set +m # disable job control in order to allow lastpipe
shopt -s lastpipe
longcommand |
  while IFS= read -r line; do lines[i]="$line"; ((i++)); done
echo "${lines[1]}" # second line
2
Hauke Laging