成長するファイルを読み込み続ける - Perl

例えばApacheのアクセスログやエラーログなどは、アクセスがある限り半永久的にデータがファイルに追記(記録)されていく。このような成長し続けるファイルを常に読み込み、表示し続けるにはどうすれば良いだろうか?

原理は簡単だ。 while(<FILE>){print ;}のようなプログラムの場合、ファイルの終端を表すEOF(End Of File)が来たタイミングでPerlの内部にフラグが立ち、それ以上ファイルポインタが先に進まなくなってしまう。

ところが一旦EOFが来たら、seek関数を実行するだけでこのフラグが消えるという性質がある。これを利用するのだ。

サンプル

#!/usr/bin/perl

;#
;#成長するファイルを追いかける
;#

#------------------------------------------------#
#ライブラリ
#------------------------------------------------#
use strict;

#------------------------------------------------#
#メインルーチン
#------------------------------------------------#
package main;{
    my $file = $ARGV[0] || usage();
    my $sec  = $ARGV[1] || 3;
    my $position = 0;

    #----------------------------#
    #       エラーチェック       #
    #----------------------------#
    die "Not FILE PATH"         if( $file !~ /^([a-zA-Z0-9¥.¥_¥/¥-]{1,})$/ );
    die "Not found ($file)"     if( not -f $file );
    die "Can not READ ($file)"  if( not -r $file );
    die "Not INTEGER ($sec)"    if( $sec and $sec !~ /^([0-9]{1,})$/  );

    #----------------------------#
    #          追跡開始          #
    #----------------------------#
    open(DAT, $file) or die("Can not open ($file) $!");
    for(;;){
        seek(DAT, $position, 0);   #seekするとEOFフラグも解除される

        while(<DAT>){              #現在の末尾(EOF)まで処理する
            #-- 表示 --#
            print ;

            #-- 復帰用に現在位置を取得 --#
            $position = tell(DAT);
        }
        sleep( $sec );
    }
    close(DAT);    #実行されない。気持ち悪いならシグナル処理を入れる

    exit;
}


#------------------------------------------------#
#[function]使用説明、終了
#------------------------------------------------#
sub usage {
    print <<'END_OF_TXT';
使用方法: endless "file" [second]
---
成長するファイルを Ctrl + C などで終了されるまで表示し続ける。
(ログファイルなどをエンドレスにcatする。end-less)
---
  file ..... ファイルのパス。必須。
  second ... ファイルの成長が早い場合は少なく(1〜2)
             そうでない場合は大きな値を設定する(5〜)
             未指定の場合は3。
END_OF_TXT

    exit;
}

まず無限ループ中でファイルを最初から最後まで全て処理する。その後ファイルを閉じないでsleep。EOFフラグ解除のためfseek。ループの最初にもどりまたファイルを表示する処理を行うことで実現できる。ちょっとしたデーモンのように動作する。
 ※Ctrl+Cなどで終了させること。

用途としては例えばエラーログを常に表示する、ファイルにデータが追記された瞬間に何らかの処理を走らせるトリガーとして使用できる。指定した時間にプログラムを実行してくれるcronでは間に合わない要件を満たさないなどの状況に出くわしたらこういった手段も検討すると良いかもしれない。
 ※個人的にはデバグ程度にとどめるのをオススメするが。

余談だが、Perlクックブックにあった以下のコードがこちらの環境(Debian3.2 + Perl5.8)で動作しなかった。

exit if( (stat(FILE))[3] ==3 )

ファイルが削除された、または読み込めなくなった際の処理が必要なのだが、ファイルテスト演算子などもうまく動作しないようだ。これらが分かる方はアドバイスなど求む。