linuxBean14.04(185)ディレクトリツリー内の一番古いファイルを抽出する方法

2018-09-30

旧ブログ

t f B! P L
ディレクトリをまたいだ一番古いファイルを抽出するのは簡単にはいきませんでしたが、結局51万強のファイルから1分半で一番古いファイルのパスを取得できました。

前の関連記事:linuxBean14.04(184)デュアルディスプレイを使う


ディレクトリツリー内のファイル総数を取得する


まず実験に使うディレクトリツリー内にあるファイル総数を求めてみます。

対象ディレクトリはホームディレクトリです。

find ~ -type f | wc -l
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f | wc -l
find: `/home/pq/.cache/dconf': 許可がありません
511814

real 1m50.799s
user 0m2.056s
sys 0m11.596s
時間測定のためにtimeコマンドを最初に付けて実行しました。

51万1814個ものファイルがありました。

「許可がありません」といわれているパスは、rootのみアクセスが許可されているフォルダでした。

ファイル数のカウントだけで1分59秒もかかっています。

find ~ -type f -exec wc -l {} +

パイプを使わずにやろうとするとファイルパスが出力されるので時間がかかり過ぎて途中でやめました。

find ~ -type f | xargs wc -l

これもファイルパスが出力されてしまいました。

ディレクトツリー内の一番古いファイルを抽出する


find ~ -type f | xargs ls -t | tail -1

これは「そのようなファイルやディレクトリはありません」がたくさんでてきます。

原因はパスにある空白です(Linux - ls: cannot access(49015)|teratail)。

find ~ -type f -exec ls -t {} + | tail -1
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f -exec ls -t {} + | tail -1
find: `/home/pq/.cache/dconf': 許可がありません
/home/pq/.local/share/mime/packages/Overrides.xml

real 2m57.982s
user 0m11.412s
sys 0m17.620s
2分57秒かかって、/home/pq/.local/share/mime/packages/Overrides.xmlが出力されました。

しかしこれはディレクトリツリー内の一番古いファイルではなく、findからlsにまとまって渡された最後のまとまりの引数のパスの中で一番古いファイルというだけでした。

find ~ -type f -printf '%T+ %p\n' | sort | head -n 1
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f -printf '%T+ %p\n' | sort | head -n 1
find: `/home/pq/.cache/dconf': 許可がありません
1980-01-01+00:00:00.0000000000 /home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.json

real 3m40.115s
user 0m29.284s
sys 0m19.056s
3分40秒もかかりましたが、/home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.jsonが正しい一番古いファイルのようです。

ファイルの日付も出力されているため、この結果を利用するにはパスのみ取り出す必要があります。

linux - How can I find the oldest file in a directory tree - Super Userで紹介されていた3つの方法をやってみました。

find ~ -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f -printf "%T+ %p\0" | sort -z | grep -zom 1 ".*" | cat
find: `/home/pq/.cache/dconf': 許可がありません
1980-01-01+00:00:00.0000000000 /home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.json

real 3m44.788s
user 0m30.096s
sys 0m19.552s
これも3分44秒もかかっています。

find ~ -type f -printf "%T@ %T+ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f -printf "%T@ %T+ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
find: `/home/pq/.cache/dconf': 許可がありません
1980-01-01+00:00:00.0000000000 /home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.json

real 4m20.295s
user 0m27.500s
sys 0m15.728s 
%T+(日付と時刻)より%T@(Jan. 1, 1970, 00:00 GMT からの経過秒数)の出力をソートした方が、速いと解説されていますが、さっきよりも時間がかかっています。

これは%T+と%T@の両方を出力しているからと考えて、%T@だけの出力にしてみました。

find ~ -type f -printf "%T@ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
pq@pq-VirtualBox:~/_tmp$ time find ~ -type f -printf "%T@ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //'
find: `/home/pq/.cache/dconf': 許可がありません
/home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.json

real 1m22.017s
user 0m14.144s
sys 0m9.568s
1分22秒という圧倒的に速くなりました。

最後のsedコマンドで%T@の出力を削っているのでパスのみの出力になっています。

この方法が一番目的に適っています。

stat -c "%y %n" "$(find ~ -type f -printf "%T@ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"
pq@pq-VirtualBox:~/_tmp$ time stat -c "%y %n" "$(find ~ -type f -printf "%T@ %p\0" | sort -nz | grep -zom 1 ".*" | sed 's/[^ ]* //')"
find: `/home/pq/.cache/dconf': 許可がありません
1980-01-01 00:00:00.000000000 +0900 /home/pq/.config/chromium/CertificateTransparency/680/_metadata/verified_contents.json

real 1m32.917s
user 0m13.616s
sys 0m6.724s
この方法は一番古いファイルの日時を出力しているのにもかかわらず1分32秒で済んでいます。

この方法は先ほどの方法で出力したファイルの日時をstatで取得して出力しているので、その分10秒ロスしています。

参考にしたサイト


ディレクトリ内のファイル数をカウントする
いくつかの方法の速度比較があります。

timeコマンドでプログラムの実行時間を知る
シェルではtimeコマンドで簡単に実行速度測定できます。

timeコマンドにはシェルの組み込みコマンドとGNU版の2種類ある -- ぺけみさお
linuxBean14.04ではシェルスクリプト内でtimeを使うとGNU版と同じ結果になりました。ただし、shebangを#!/bin/bashにするとシェル版になりました。

Linux - xargsとパイプの違いについて(63845)|teratail
xargsでしかパイプできないコマンドの例があります。

find の -exec optionの末尾につく \; と + の違い。
+がxargsに該当します。

Linux - ls: cannot access(49015)|teratail
パスにスペースがあるとパイプでそのまま渡すとエラーになります。

linux - How can I find the oldest file in a directory tree - Super User
%T@でソートする方法が一番速いようです。

bash - How to process each line received as a result of grep command - Stack Overflow
grepの結果をwhile文で1行ずつ処理する方法。

次の関連記事:linuxBean14.04(186)指定時間内に更新されたファイルを指定フォルダに抽出する方法

ブログ検索 by Blogger

Translate

最近のコメント

Created by Calendar Gadget

QooQ