find(1)ねたというのは、定期的にblogosphereを賑わせるものだし、それはそれでよいし、私自身いくつか書いているのだけど....
- findを極める! - IDEA*IDEA 〜 百式管理人のライフハックブログ 〜
- タグ「find」を含む新着エントリー - はてなブックマーク
- 404 Blog Not Found:perl - the best friend of find(1)
- 404 Blog Not Found:勝手に添削 - find(1)
さすがに何年もblogosphereにいると、いいかげん見飽きてくる。ましてや「極めたい」ともなると、findばっか見ていても絶対無理なのだし。
というわけで、findとは何かを改めて説明したあと、その背後にあるstatを抑えてみることにする。
findとは何か
findは、その名のとおり「ファイルを見つける」コマンド--ではない。ファイルを見つけることも出来るが、それはfindの本質ではない。そのことは、実はfindの man page にもきちんと書いてある。
NAME
find -- walk a file hierarchy
「ファイル階層をたぐる」。本質はこちらにある。具体的にfindのやっていることを見るとこうなる
- 指定されたノードおよびその下にあるノード全てに対し
- 指定された表現で評価し
- 真になったノードに、指定された操作を適用する
3.のデフォルトが「そのノードのパスを表示」することであるのは、一度でもfindしてみた人であればわかるだろう。そしてfindの難しい部分が、2であることも。
この2.の部分のキモとなるのが、statなのである。
statとは何か
ノードには、大きく分けて二種類の属性がある。データとメタデータだ。データが「ファイル」の中味であり、それ以外はメタデータということになる。ファイルがいつ作成され、いつ更新されたか。サイズはどれくらいなのか。ファイルの中味以外の情報は、全てメタデータということになる。
findが確認するのは、そのうちメタデータのみである。findは中味を一切吟味しない。中味まで検索したかったら別のコマンドがいる。たとえば Mac OS X ならSpotlightや、そのコマンドライン版である mdfind(1) がこの目的に使えるし、それ以外でも Google Desktop がある。
そのfindがパス以外に見ているのが、stat()、厳密にはlstat()の結果なのだ。ちなみにlstatとstatの違いは、ノードが symlink だった場合に、symlinkそのものの情報を提示するか(lstat)、symlink先の情報を提示するか(stat)である。
それでは、statでわかるメタデータはどうなっているのだろうか?実はOSによってその中味は微妙に異なるのだが、perlの組み込みコマンドであるstat()が、よき最大公約数となっている。
Not all fields are supported on all filesystem types. Here are
the meanings of the fields:
0 dev device number of filesystem
1 ino inode number
2 mode file mode (type and permissions)
3 nlink number of (hard) links to the file
4 uid numeric user ID of file's owner
5 gid numeric group ID of file's owner
6 rdev the device identifier (special files only)
7 size total size of file, in bytes
8 atime last access time in seconds since the epoch
9 mtime last modify time in seconds since the epoch
10 ctime inode change time in seconds since the epoch (*)
11 blksize preferred block size for file system I/O
12 blocks actual number of blocks allocated
それでは、順を追って解説していこう。
- dev
- ino
devはヴォリュームごとに固有に割り当てられるIDであり、inoはそのボリューム内で固有に割り当てられるIDである。Unixのファイルシステムにおいて「同じファイル」というのは、この二つが一致していることを意味する。
findにおいては-xdevと-inumがこれに関連する。 - mode
ファイルの種類とパーミッションがここに格納される。下12バイトがパーミッション、残りがファイルの種類である。パーミッションに関しては404 Blog Not Found:unix - permissionあれこれを参照のこと。
findに関連するオプションは、-typeと-permとなる。 - nlink
そのノードにハードリンクがいくつあるかを示す。Unixの場合、一つのファイルに対し複数のパスを持たせることが出来、そしてこのリンク数がゼロ、かつそのファイルを開いているプロセスがなくなった時点でファイルが消去されたこととなる。Unixにおけるファイル削除のシステムコール名が
deleteではなくunlinkとなっているのはそのためだ。findにおいては-linksがこれに関連する。 - uid
- gid
ファイルの所有者IDおよびグループID。
statの返す値はIDであり、名前でない点に注意が必要である。ユーザー名からUIDを調べるには、perlでは(getpwnam($username))[2]とし、グループ名からGIDを調べるには(getgrnam($groupname))[2]とすればよい。findでは、-userと-groupがこれに対応する。ありがたいことに、名前とIDの双方を引数として受け付ける。このことがあるので、IDと紛らわしい名前、例えばユーザー名404などというのは避けるべきである。 - rdev
デバイス識別子。おそらくstatの戻り値の中で最も用いられないフィールドではあるが、存在ぐらいは知っておいてもよいだろう。
findに対応するオプションはない。ちなみにこれが何かというのは、/dev以下をls -lしてみればわかる。 - size
ファイルサイズ。
findでは-sizeがこれに対応。後述のblksizeとblocksも参照のこと。 - atime
- mtime
- ctime
それぞれ「最後にアクセスされた時間」「データ最終更新時間」、「メタデータも含めた最終更新時間」を、Unix Epoch = 1970.01.01 00:00:00 GMTからの経過秒数で。
findでは-atime,-mtime,-ctimeがそれぞれに相当する。ただしatimeは、パフォーマンス優先のヴォリュームではnoatimeオプション付きでマウントされていたりするので、あまりあてにしない方がよいだろう。なお、atimeとmtimeは、
utime(3)で変更可能である。perlだとutime $atime, $mtime, @pathsとする。もう一つの注意点として、
findにおけるこれらのオプションの引数は、stat()が返す絶対時間ではなく、findコマンドが実行された時間からの相対時間だということがある(perlだと-A,-M,-C演算子相当)。絶対時間を使いたかったら、素直にperlなどでスクリプトを書く方がよいだろう。 - blksize
- blocks
この二つをかけたものが、実際にファイルシステム上でそのノードが使っているディスク領域の大きさということになる。たとえ 1 byte しかないファイルでも、実際には4K byteほどの領域を使ってしまうのはそのためだ。逆に sparse file をサポートしている場合、sizeが1GBとかあったとしても1 blockしか使っていないということもありうる
残念ながらfindはこれに対応していないようだ。その代わりduコマンドをこの目的に使うことが出来る。
以上を抑えておけば、findを極めるどころか、findを再実装しなおすことも出来るだろう。enjoy!
Dan the Man with Too Many Files to stat
fromdusktildawnのこの↓エントリdisってるんだとおもったら違ったw
http://d.hatena.ne.jp/fromdusktildawn/20090512/
ですよねー。