ファイルの更新完了を検知する

投稿日:2019年04月14日 18時21分11秒

ここ2ヶ月perlのお仕事をしています。こんなにPerl書いた事はなかったのですがそれなりに使えますね。ライブラリが充実してるのでなるべくライブラリに任すと簡単に処理が書けます。今回はファイル更新の検知について割とうまく書けたのでメモしておきます。
まずファイル更新検知ですが、Win32::ChangeNotifyを使う事でファイル検知ができます。$filterは検知する対象です。今回はファイルとフォルダの生成、削除のみ検知することにしました。

my $filter = "FILE_NAME DIR_NAME";
my $notify = Win32::ChangeNotify->new($targetPath, $flgSubTree, $filter);
if( $notify->wait( $timeout_Notify ) ){
#ファイル一覧取得
#更新処理
}

ここで検知してるのはファイル作成等の場合なので当然ファイル更新中もありえます。通常は他フォルダにファイルを更新して更新が終わったらフォルダ毎moveしたりします。しかし今回は仕様上作成中のファイルも扱う事になってしまいました。よってファイルが書き込み中でも検知してしまいます。これを解決するためにファイルが更新中なのかどうかを検知します。
File-Download-and-Password-Generator-Class-Update

ファイルが更新中かどうか色々考えた挙句同じ名前にリネームすることにしました。今回のターゲットはWindowsですが他のOSでもオープン中のファイルはリネームに失敗すると思います。なおかつリネームはシステムコールなので実施するコストが極めて低いと考えられます。

sub wait_until_close
{
$file = shift;
$wait = 0.2;
$time_out = 3;
until(not -e $file or my $stat = rename($file,$file) or $spend_time > $time_out ){
select(undef,undef,undef,$wait);
$spend_time += $wait;
}
return ($spend_time < $time_out ); }

ミソは同じファイル名にrenameするところ。ファイルのオープン中ではrenameは許可されません。なおかつファイル更新日時等は変更されません。また成功しても副作用がありません。更新日などはそのまま保存されます。
selectはperl版の1秒以下sleepです。よってタイムアウト、ファイル消去、ファイルがクローズされるまではループを抜けません。
この方法は他の言語でもOSでも利用できるでしょう。非同期更新に悩んでいる場合には検討されてはいかがでしょうか。

[<< 人間は言葉が同じなためCP932を始めた。人々の言語を乱し通じない違う文字列を話させるようにしよう]

[Perlコードを最適化する >>]