Perl Tips: tail -fのエミュレーション
2023/12/23
ファイル出力を監視/表示したい
  • Linuxを使用していると何らかのツールの実行ログを監視するのに'tail -f ログファイル名'コマンドをよく使用します。

  • これと似たことをPerlのスクリプトから行う場合、IO::Tailモジュールを使用するのが良さそうですが、実際使ってみると思ったように動いてくれません。使い方の思想を読み取りきれませんでした。

  • そこでtellとseek関数を使用することに。ヒントはseek関数のperldocに書かれていました。

  • ちなみにやりたかった事は「ファイル全体を表示して追加があれば追従する」というものなので、厳密には'tail -f'と違いますがご容赦ください。

  • コードですが、下記の3つがあります。test1.plが該当コードを持ちます
    • test0.pl: 監視対象ファイル作成。1秒毎に数字追加。20秒まで出力後'EOF'文字列を出力して終了。
    • test1.pl: 監視対象ファイルを読んで追記あれば表示。EOF文字列を見つけたら終了。
    • runme.bat: test0.plを別プロセスで実行開始、5秒後にtest1.plを実行するバッチファイル。

  • test0.plのコード: autoflush有効にしています。
  • #!perl -w
    use IO::Handle;
    use strict;
    
    {
        my $fh;
        open($fh, "> test0.txt") or die;
            $fh->autoflush(1);  # autoflush 有効
            for (my $i=0; $i<20; $i++) {
                print     "$i\n";  # 標準出力
                print $fh "$i\n";  # ファイルハンドル
                sleep(1);          # 1秒待ち
            }
            print $fh "EOF\n";     # 最後に文字列'EOF'を出力
        close($fh);
    }
    

  • test1.plのコード: 追従表示実装部。1秒未満sleepのためにusleepも使用。
  • my @data = <$fh>でファイルハンドルがEOF状態になっているのを、seek実行でEOF解除しているのがポイントです。実は読み取り後のポジションは動かしていません。EOF解除のためにseekをダミー実行しているのです。
  • #!perl -w
    use Time::HiRes;
    use strict;
    
    {
        my $fh;   # ファイルハンドル
        my $pos;  # ファイルポジション
        
        open($fh, "< test0.txt") or die;
            while(1) {
                my @data = <$fh>;   # 現ポジションから最後までデータ取得(ファイルハンドルはEOFになる)
                print @data;        # 読み出しデータ表示
                $pos = tell($fh);   # 現読み出し終了ポジション取得
                
                my $join_data = join('', @data);
                last if ($join_data =~ /EOF/);  # EOF文字列を見つけたらループ終了
                
                Time::HiRes::usleep(10*1000);   # 10*1000us = 10ms待ち
                
                seek($fh, $pos, 0);  # 現読み出し終了ポジションへのseekダミー実行で
                                     # 真の目的であるファイルハンドルのEOF解除を実施!!
            }
        close($fh);
    }
    
  • runme.bat: startコマンドでtest0.plを別プロセスとして実行しています。
  • @echo off
    del test0.txt >nul 2>&1
    
    echo #=======================================#
    echo # test0.pl: test0.txt Writeプロセス開始 #
    echo #=======================================#
    start test0.pl
    
    echo.
    echo #==========#
    echo # 5秒 wait #
    echo #==========#
    timeout /T 5
    
    echo.
    echo #======================================#
    echo # test1.pl: test0.txt Readプロセス開始 #
    echo #======================================#
    test1.pl
    >runme.bat
    #=======================================#
    # test0.pl: test0.txt Writeプロセス開始 #
    #=======================================#
    
    #==========#
    # 5秒 wait #
    #==========#
    
    0 秒待っています。続行するには何かキーを押してください ...
    
    #======================================#
    # test1.pl: test0.txt Readプロセス開始 #
    #======================================#
    0
    1
    2
    ...中略...
    18
    19
    EOF
Copyright(C) 2023 Altmo
本HPについて