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行ずつステータスを表示します。 longcommand
completesの前に出力をキャッチしたいと思います。
このようにして、期待する結果が得られない場合、longcommand
を強制終了できます。
私が試してみました:
longcommand |
while IFS= read -r line
do
whatever "$line"
done
ただし、whatever
(例:echo
)は、longcommand
が完了した後にのみ実行されます。
コマンドを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"
}
lastpipe
にbash
を設定するHaukeの例は、別のソリューションです。
コマンドの出力を「発生時に」処理していることを確認するには、stdbuf
を使用してプロセスを設定できますstdout
をラインバッファリングします。
stdbuf -oL longcommand |
while IFS= read -r line
do
whatever "$line"
done
これにより、内部で出力をブロックにバッファリングする代わりに、パイプに一度に1行を書き込むようにプロセスが構成されます。プログラムがこの設定自体を内部で変更できることに注意してください。 unbuffer
(expect
の一部)またはscript
でも同様の効果を得ることができます。
stdbuf
はGNUおよびFreeBSDシステムで使用できます。これはstdio
バッファリングにのみ影響し、非setuid、非-動的にリンクされる-setgidアプリケーション(LD_PRELOADトリックを使用するため)。
#! /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