web-dev-qa-db-ja.com

bashスクリプトの実行中に出力をファイルに強制的にフラッシュする

次のコマンドを使用してcrontabによって毎日呼び出される小さなスクリプトがあります。

/homedir/MyScript &> some_log.log

このメソッドの問題は、some_log.logがMyScriptの終了後にのみ作成されることです。実行中にプログラムの出力をファイルにフラッシュして、次のようなことができるようにします

tail -f some_log.log

進行状況などを追跡します.

69
olamundo

bash自体が実際にログファイルに出力を書き込むことはありません。代わりに、スクリプトの一部として呼び出されるコマンドは、出力を書き込み、必要に応じてフラッシュします。したがって、あなたの質問は、bashスクリプト内のコマンドを強制的にフラッシュする方法です。それは、それらが何であるかに依存します。

27
Chris Dodd

これに対する解決策を見つけました here 。 OPの例を使用して、基本的に実行します

stdbuf -oL /homedir/MyScript &> some_log.log

そして、出力の各行の後にバッファーがフラッシュされます。私はこれをNohupと組み合わせて、リモートマシンで長いジョブを実行することがよくあります。

stdbuf -oL Nohup /homedir/MyScript &> some_log.log

この方法では、ログアウト時にプロセスがキャンセルされません。

66
Martin Wiebusch

script -c <PROGRAM> -f OUTPUT.txt

キーは-fです。 manスクリプトからの引用:

 -f, --flush
         Flush output after each write.  This is Nice for telecooperation: one person does `mkfifo foo; script -f foo', and another can supervise real-time what is being done
         using `cat foo'.

バックグラウンドで実行:

Nohup script -c <PROGRAM> -f OUTPUT.txt
21
user3258569

teeを使用して、フラッシュする必要なくファイルに書き込むことができます。

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null
9
crenate

これはbashの機能ではありません。シェルは問題のファイルを開き、ファイル記述子をスクリプトの標準出力として渡すだけです。あなたがする必要があるのは、出力がフラッシュされることを確認することですスクリプトから現在よりも頻繁に。

たとえば、Perlでは、これを次のように設定することで実現できます。

$| = 1;

これについての詳細は perlvar をご覧ください。

3
Greg Hewgill

これは役に立ちますか?

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

これにより、access.logから一意のエントリがすぐに表示されます
http://www.pixelbeat.org/programming/stdio_buffering/stdbuf-man.html

2
Ondra Žižka

出力のバッファリングは、プログラム/homedir/MyScriptが実装されています。出力がバッファリングされていることがわかった場合は、実装でそれを強制する必要があります。たとえば、pythonプログラムの場合はsys.stdout.flush()を使用し、Cプログラムの場合はfflush(stdout)を使用します。

2
Midas

ありがとう@user3258569、スクリプトは、おそらくbusyboxで機能する唯一のものです!

しかし、シェルはその後、私のために凍結していました。原因を探して、私はこれらの大きな赤い警告を「非対話式シェルで使用しないでください」と見つけました スクリプトマニュアルページ

scriptは、主にインタラクティブなターミナルセッション用に設計されています。 stdinが端末ではない場合(例:echo foo | script)、スクリプトセッション内のインタラクティブシェルが[〜#〜] eof [〜#〜]およびscriptには、セッションを閉じるタイミングがわかりません。詳細については、[〜#〜] notes [〜#〜]セクションを参照してください。

本当です。 script -c "make_hay" -f /dev/null | grep "needle"は私のためにシェルを凍結していました。

警告に反して、私はecho "make_hay" | script EOFに合格するので、試しました

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

そしてそれは働いた!

Manページの警告に注意してください。これはあなたのために働かないかもしれません。

1

見つけた方法 ここ 問題は、スクリプトから実行するプログラムがジョブを終了するまで待つ必要があることです。
スクリプト内でbackgroundでプログラムを実行すると、さらに試行することができます。

一般に、終了する前にsyncを呼び出すと、ファイルシステムバッファーをフラッシュでき、少し役立ちます。

スクリプトでbackground&)でいくつかのプログラムを起動する場合、 wait スクリプトを終了する前に終了すること。それがどのように機能するかについてのアイデアを得るには、以下を見ることができます

#!/bin/bash
#... some stuffs ...
program_1 &          # here you start a program 1 in background
PID_PROGRAM_1=${!}   # here you remember its PID
#... some other stuffs ... 
program_2 &          # here you start a program 2 in background
wait ${!}            # You wait it finish not really useful here
#... some other stuffs ... 
daemon_1 &           # We will not wait it will finish
program_3 &          # here you start a program 1 in background
PID_PROGRAM_3=${!}   # here you remember its PID
#... last other stuffs ... 
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3  # program 2 is just ended
# ...

waitはジョブとPID番号で機能するため、スクリプトの最後に遅延ソリューションを配置する必要があります

for job in `jobs -p`
do
   wait $job 
done

すべてのchildプロセスの終了を検索して待機する必要があるため(バックグラウンドで何か他の何かを実行する場合)、より困難な状況です:forたとえば、daemonを実行すると、おそらく終了するのを待つことはできません:-)。

注意:

  • wait $ {!}は、「最後のバックグラウンドプロセスが完了するまで待つ」ことを意味します。$!は、最後のバックグラウンドプロセスのPIDです。 wait ${!}の直後にprogram_2 &を置くことは、program_2でバックグラウンドで送信せずに&を直接実行することと同等です。

  • wait の助けから:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    
1
Hastur

stdbufの代替はawk '{print} END {fflush()}'です。これを行うための組み込みのbashがあればいいのにと思います。通常は必要ありませんが、古いバージョンではファイル記述子にbash同期バグがある可能性があります。

0
Brian Chrisman