ファイルを再帰的に更新時刻で並べ替えるワンライナー
ファイルの更新時刻順に並べ替えたいことは結構あると思うのだが、フォルダを下って再帰的に行う場合はどうすればいいのだろうか?
まあWindowsのExplorerやMacのFinderだと、フォルダを全て開いて変更日の列をクリックとかすればいいかも。ただファイルの数が多いとフォルダを開くのは大変かも?ではFreeBSDやLinuxのCUIなシェルの場合はどうするか?
ls
コマンドには-t
オプションがあり、ファイルの更新時刻の新しい順で並べ替えてくれる。だが、ディレクトリを再帰的に下る-R
オプションと併用しても、同じディレクトリ内しか並べ替えてくれない。
find
コマンドはディレクトリを再帰的に下っていくことができるが、ファイルの更新時刻順に並べ替える機能がない?(あったら教えていただけるとありがたや・・・)
んむむ、ls
もfind
も片手落ちじゃないか・・・ということでどうすればいいか?
お題
下記のような状態のファイルとディレクトリ構成の場合で実験。
$ ls -Rl DIR total 0 drwxr-xr-x+ 5 papiro staff 170 9 2 00:22 dir1 drwxr-xr-x+ 4 papiro staff 136 9 2 00:22 dir2 -rw-r--r--+ 1 papiro staff 0 9 1 08:00 file1 -rw-r--r--+ 1 papiro staff 0 9 2 00:16 file2 -rw-r--r--+ 1 papiro staff 0 10 24 2014 file3 DIR/dir1: total 0 -rw-r--r--+ 1 papiro staff 0 9 2 00:20 file4 -rw-r--r--+ 1 papiro staff 0 12 31 2014 file5 -rw-r--r--+ 1 papiro staff 0 12 31 2013 file6 DIR/dir2: total 0 -rw-r--r--+ 1 papiro staff 0 12 31 2013 file7 -rw-r--r--+ 1 papiro staff 0 1 1 2015 file8
最初の思いつき(だがダメな場合も)
最初に思いついたのは、下記のようにxargs
を使ってfind
とls
の連携。これでいいじゃん?と思ったのだが・・・
$ find DIR -type f | xargs ls -tl -rw-r--r--+ 1 papiro staff 0 9 2 00:20 DIR/dir1/file4 -rw-r--r--+ 1 papiro staff 0 9 2 00:16 DIR/file2 -rw-r--r--+ 1 papiro staff 0 9 1 08:00 DIR/file1 -rw-r--r--+ 1 papiro staff 0 1 1 2015 DIR/dir2/file8 -rw-r--r--+ 1 papiro staff 0 12 31 2014 DIR/dir1/file5 -rw-r--r--+ 1 papiro staff 0 10 24 2014 DIR/file3 -rw-r--r--+ 1 papiro staff 0 12 31 2013 DIR/dir1/file6 -rw-r--r--+ 1 papiro staff 0 12 31 2013 DIR/dir2/file7
これは、ディレクトリとファイルが少ない時だけ有効。ディレクトリとファイルが大量にあるとダメなのだ。
xargs
の動きを思い出すと、シェルの引数の長さの制限いっぱい毎にコマンドを複数回実行している。仮にファイルの引数制限が1000個だとすると、下記のようなコマンドが実行されている。
ls -tl file0001 file0002 .... file1000 ls -tl file1001 file1002 .... file2000 ls -tl file2001 file2002 .... file3000
つまり1000個ずつで更新時間の並べ替えが行われており、全体としての並べ替えにはなっていない。
じゃあどげするの?
じゃあファイル更新時刻の文字列情報を使って、sort
コマンドで並べ替えてやればいいかも?しかしls
コマンドの-l
オプションで更新時刻を表示は意外と厄介。
$ ls -l DIR total 0 drwxr-xr-x+ 5 papiro staff 170 9 2 00:22 dir1 drwxr-xr-x+ 4 papiro staff 136 9 2 00:22 dir2 -rw-r--r--+ 1 papiro staff 0 9 1 08:00 file1 -rw-r--r--+ 1 papiro staff 0 9 2 00:16 file2 -rw-r--r--+ 1 papiro staff 0 10 24 2014 file3
8列目の表示が困ったちゃんで、更新時刻が1年より新しいと時分、古いと年を表示する。6列目と7列目も2桁表示してくれない。sort
コマンドで扱いにくい。
ということで、更新時刻表示を固定長で表示する方法があればいいのでは?
FreeBSD
最近のFreeBSDでは、ls
コマンドの-D
オプションで、更新時刻の出力フォーマットが指定できる。ということで下記のシェル芸でオケ。
$ find DIR -type f | xargs ls -lD '%Y%m%d%H%M%S' | sort -k6,6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir1/file6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir2/file7 -rw-r--r-- 1 papiro wheel 0 20141024080000 DIR/file3 -rw-r--r-- 1 papiro wheel 0 20141231235959 DIR/dir1/file5 -rw-r--r-- 1 papiro wheel 0 20150101000000 DIR/dir2/file8 -rw-r--r-- 1 papiro wheel 0 20150901080000 DIR/file1 -rw-r--r-- 1 papiro wheel 0 20150902001605 DIR/file2 -rw-r--r-- 1 papiro wheel 0 20150902002011 DIR/dir1/file4
Linux
LinuxというかGNUのls
コマンドだと--time-style
オプションを使うことでdateコマンドと同様なフォーマット指定が可能。ということで下記のシェル芸でオケ。
$ find DIR -type f | xargs ls -l --time-style='+%Y%m%d%H%M%S' | sort -k6,6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir1/file6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir2/file7 -rw-r--r-- 1 papiro wheel 0 20141024080000 DIR/file3 -rw-r--r-- 1 papiro wheel 0 20141231235959 DIR/dir1/file5 -rw-r--r-- 1 papiro wheel 0 20150101000000 DIR/dir2/file8 -rw-r--r-- 1 papiro wheel 0 20150901080000 DIR/file1 -rw-r--r-- 1 papiro wheel 0 20150902001605 DIR/file2 -rw-r--r-- 1 papiro wheel 0 20150902002011 DIR/dir1/file4
また--full-time
オプションだと下記のような固定長で日付と時刻表示可能。ということで下記でもオケ。
$ find DIR -type f | xargs ls --full-time | sort -k6,7 -rw-r--r-- 1 papiro wheel 0 2013-12-31 23:59:59.000000000 +0900 DIR/dir1/file6 -rw-r--r-- 1 papiro wheel 0 2013-12-31 23:59:59.000000000 +0900 DIR/dir2/file7 -rw-r--r-- 1 papiro wheel 0 2014-10-24 08:00:00.000000000 +0900 DIR/file3 -rw-r--r-- 1 papiro wheel 0 2014-12-31 23:59:59.000000000 +0900 DIR/dir1/file5 -rw-r--r-- 1 papiro wheel 0 2015-01-01 00:00:00.000000000 +0900 DIR/dir2/file8 -rw-r--r-- 1 papiro wheel 0 2015-09-01 08:00:00.000000000 +0900 DIR/file1 -rw-r--r-- 1 papiro wheel 0 2015-09-02 00:16:05.000000000 +0900 DIR/file2 -rw-r--r-- 1 papiro wheel 0 2015-09-02 00:20:11.000000000 +0900 DIR/dir1/file4
Mac
MacのコマンドはBSD系だからFreeBSDと同じで出来るかも?と思ったが、ls
コマンドに-D
オプションが使えなかった。Macに搭載されているls
コマンドはバージョンが古いためか?なのでhomebrewでGNU coreutilsを入れ、GNUのls
コマンドgls
を使ってLinuxと同じシェル芸でオケ。
$ find DIR -type f | xargs gls -l --time-style='+%Y%m%d%H%M%S' | sort -k6,6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir1/file6 -rw-r--r-- 1 papiro wheel 0 20131231235959 DIR/dir2/file7 -rw-r--r-- 1 papiro wheel 0 20141024080000 DIR/file3 -rw-r--r-- 1 papiro wheel 0 20141231235959 DIR/dir1/file5 -rw-r--r-- 1 papiro wheel 0 20150101000000 DIR/dir2/file8 -rw-r--r-- 1 papiro wheel 0 20150901080000 DIR/file1 -rw-r--r-- 1 papiro wheel 0 20150902001605 DIR/file2 -rw-r--r-- 1 papiro wheel 0 20150902002011 DIR/dir1/file4 $ find DIR -type f | xargs gls --full-time | sort -k6,7 -rw-r--r-- 1 papiro wheel 0 2013-12-31 23:59:59.000000000 +0900 DIR/dir1/file6 -rw-r--r-- 1 papiro wheel 0 2013-12-31 23:59:59.000000000 +0900 DIR/dir2/file7 -rw-r--r-- 1 papiro wheel 0 2014-10-24 08:00:00.000000000 +0900 DIR/file3 -rw-r--r-- 1 papiro wheel 0 2014-12-31 23:59:59.000000000 +0900 DIR/dir1/file5 -rw-r--r-- 1 papiro wheel 0 2015-01-01 00:00:00.000000000 +0900 DIR/dir2/file8 -rw-r--r-- 1 papiro wheel 0 2015-09-01 08:00:00.000000000 +0900 DIR/file1 -rw-r--r-- 1 papiro wheel 0 2015-09-02 00:16:05.000000000 +0900 DIR/file2 -rw-r--r-- 1 papiro wheel 0 2015-09-02 00:20:11.000000000 +0900 DIR/dir1/file4