Digitra

LINUXサーバの設定やプログラムのことなどを中心にブログを書いています。

perlのreaddirでのファイル取得順序がバラバラ?

バッチ処理で特定のディレクトリ以下のファイルをDBに取り込みをしていたのだが、 取り込む順序があるのにも関わらず、perl の readdirで得られたファイルの順序で取り込みをしていて不具合が発生していた。

 

 

perlのreadirの仕様はどうなってるのか? 

そもそも取り込み順にソートしていないことが問題ではあるのだが、一体perlのreaddirはどういった順序でファイルを取得しているのか、試しにサンプルプログラムで確認してみた。

test.pl

#!/usr/bin/perl

$dir = "/tmp/";
opendir(DIR, $dir);
@files = grep { !m/^(\.|\.\.)$/g } readdir DIR;
close DIR;
foreach $file ( @files ) {
        print $file."\n";
}

ファイルを表示してみるとタイムスタンプ順もファイル名順でもなく、よくわからない。

 

ファイルを作ってテスト

試しに以下のコマンドでテストファイルをtouchして上記のテストプログラムを実行すると、

$ mkdir /tmp/test
$ for i in `seq -w 1 10`;do touch /tmp/test/hogehoge.$i;done
$ ./test.pl
hoge_08.txt
hoge_10.txt
hoge_09.txt
hoge_07.txt
hoge_04.txt
hoge_06.txt
hoge_02.txt
hoge_01.txt
hoge_05.txt
hoge_03.txt

んー、バラバラになるなぁと、ファイル名変えたりして何度かテストしていると、

hogehoge.10
hogehoge.09
hogehoge.08
hogehoge.07
hogehoge.06
hogehoge.05
hogehoge.04
hogehoge.03
hogehoge.02
hogehoge.01

新しい順になったりと挙動がよくわからない。 調べてみたところ、ファイルシステムに準ずると言われていることもあるらしいが、perlの公式な仕様としては明確になっていないとのことだ。

 

というわけで、順序が大切なときはreaddirの取得順を当てにせずしっかりsortしよう。

 

perlで古い順にファイルをソートするには

my @sorted_files =
  map { $_->[0] }
  sort { $a->[1] <=> $b->[1] }
  map { my @st = stat "$dir/$_"; [ $_, $st[10] ] } @files;

こんな感じでstatした結果をsortする。

 

 lsコマンドでミリ秒単位で古い順に表示するには

ls -alttr --full-time

 -ttでミリ秒まで

-rで逆順ソートしてくれる

--full-time でミリ秒以下まで表示

 

参考にしたサイト