find の -permを使う tips

findの中でも有用なのだが使いにくいものに -perm オプションがある。fileの permissionを条件にするものだ。

という書き出しにしてみたが、そんなに permオプション使うかな、正直なところ。このまえ、自分が読めないファイルを見つけるために

 find . ! -perm +044

とかやったけど*1。それ以前に使ったのは1ヶ月前だしな。でも弾さんのblogでネタ振りしてくれたんで勉強がてらくらいついておこう。

前提

こんなスクリプトで "test" ディレクトリ以下に 512個ファイルを作っておく。

#!/usr/bin/perl

for ($i = 0; $i < 512; $i++) {
    $_ = sprintf("%03o", $i);
    s/0/---/g;
    s/1/--x/g;
    s/2/-w-/g;
    s/3/-wx/g;
    s/4/r--/g;
    s/5/r-x/g;
    s/6/rw-/g;
    s/7/rwx/g;
    open TOUCH, ">$_";
    close TOUCH;
    chmod $i, $_;
}

作ったファイルは以下のようになるわけだ。ファイル名と permissionが一致しているのに注意:

[11:19]jargon:find-test<212> ls test -l |head
total 0
----------  1 hujikojp hujikojp 0 2007-03-31 10:57 ---------
-------r--  1 hujikojp hujikojp 0 2007-03-31 10:57 ------r--
----r-----  1 hujikojp hujikojp 0 2007-03-31 10:57 ---r-----
-r--------  1 hujikojp hujikojp 0 2007-03-31 10:57 r--------
----r--r--  1 hujikojp hujikojp 0 2007-03-31 10:57 ---r--r--
-r-----r--  1 hujikojp hujikojp 0 2007-03-31 10:57 r-----r--
-r--r-----  1 hujikojp hujikojp 0 2007-03-31 10:57 r--r-----
-r--r--r--  1 hujikojp hujikojp 0 2007-03-31 10:57 r--r--r--
-r--r--rw-  1 hujikojp hujikojp 0 2007-03-31 10:57 r--r--rw-

8進数表記の場合

permオプションの場合、8進数表記のほうがわかりやすい気がする。これがわからない人は「404 Blog Not Found:unix - permissionあれこれ」でも見てくれ。

まず単純に普通に数値を指定すると exactly matchだ。

[12:02]jargon:find-test<215> find test -perm 444
test/r--r--r--

しかしどうでもいい bitも多いので +/- という記法もある。まず "-" は 「andみたいなもの」と覚えておくといい。

[12:03]jargon:find-test<216> find test -perm -444 |head
test
test/r--r--r--
test/r--r--r-x
test/r--r--rw-
test/r--r--rwx
test/r--r-xr--
test/r--r-xr-x
test/r--r-xrw-
test/r--r-xrwx
test/r--rw-r--

出てきたファイルすべて r..r..r..と、引数に対応した bitが立っていることがわかるだろう *2
ちょっと厳密に言うと、引数に指定したのを smask, 対象となるファイルの permissionを dmaskとすると、以下の条件のときに(かつそのときに限り) 真となる:

(smaks & dmask) == smask

演習問題 0:
以下の結果は? (test自体が入ることを忘れないよう)

find test -perm -444 | wc -l

"+" は「orみたいなもの」だ。

[12:04]jargon:find-test<217> find test -perm +444 | head
test
test/------r--
test/------r-x
test/------rw-
test/------rwx
test/-----xr--
test/-----xr-x
test/-----xrw-
test/-----xrwx
test/----w-r--
[12:06]jargon:find-test<218> find test -perm +444 | tail
test/rwxrw-rw-
test/rwxrw-rwx
test/rwxrwx---
test/rwxrwx--x
test/rwxrwx-w-
test/rwxrwx-wx
test/rwxrwxr--
test/rwxrwxr-x
test/rwxrwxrw-
test/rwxrwxrwx

ちとわかりにくいが、三つの rのうち (引数のうち、bitのたっているものに相当)、 どれかひとつでも立っていれば出力されている。先ほどと同じ風に条件を書くと以下のとおり:

(smask & dmask) != 0

演習問題 1:
以下の結果は? (test自体が入ることを忘れないよう)

find test -perm +444 | wc -l

以上、bitが立っていることしか表現できないので、bitが寝ていることを条件にするには "!" と組み合わせる。

[12:08]jargon:find-test<220> find test \! -perm +44 | tail
test/rwx--x-w-
test/rwx--x-wx
test/rwx-w----
test/rwx-w---x
test/rwx-w--w-
test/rwx-w--wx
test/rwx-wx---
test/rwx-wx--x
test/rwx-wx-w-
test/rwx-wx-wx

こうやれば、持ち主しか読めないファイルをリストアップできるわけだ。

シンボル表記の場合

こっちのほうが難しい気がして後回しにしておいたんだけど。というか、俺はいままで使い方知らなかったんで避けてたんだけど、もしかして 8進数を知らない人にはこっちのほうが簡単なのかもしれない。

いや、でも書いているうちにやっぱりよくわかんなくなってきた。わかんなくなった顛末は後ろのほうに。

まず exactly matchの場合。chmodと同じく [augo]+[rwx] だ (ほんとは "s" とかも使えるんだろうけどここは解説しない)

[12:08]jargon:find-test<221> find test -perm a+r 
test/r--r--r--
[12:12]jargon:find-test<224> find test -perm o+r 
test/------r--

"-" や "+" もつかえる

[12:11]jargon:find-test<223> find test -perm -a+r | head
test
test/r--r--r--
test/r--r--r-x
test/r--r--rw-
test/r--r--rwx
test/r--r-xr--
test/r--r-xr-x
test/r--r-xrw-
test/r--r-xrwx
test/r--rw-r--
[12:11]jargon:find-test<222> find test -perm +a+r | tail
test/rwxrw-rw-
test/rwxrw-rwx
test/rwxrwx---
test/rwxrwx--x
test/rwxrwx-w-
test/rwxrwx-wx
test/rwxrwxr--
test/rwxrwxr-x
test/rwxrwxrw-
test/rwxrwxrwx

"a+r" とかで単純に指定できるのはいいが、それよりややこしいのはどうするのか? それには"," で区切ってつなげることができる:

[12:21]jargon:find-test<268> find test -perm g+w,u+r 
test/r---w----

無論、"-"とあわせてもつかえる。"r.x.wx..x" が立っているものだけ出ているのを確認してほしい:

[12:21]jargon:find-test<269> find test -perm -g+w,u+r,a+x |head
test/r-x-wx--x
test/r-x-wx-wx
test/r-x-wxr-x
test/r-x-wxrwx
test/r-xrwx--x
test/r-xrwx-wx
test/r-xrwxr-x
test/r-xrwxrwx
test/rwx-wx--x
test/rwx-wx-wx

もちろん "+"とも...あ、あれ??

[12:21]jargon:find-test<270> find test -perm +g+w,u+r,a+x | head
test
test/--------x
test/-------w-
test/-------wx
test/------r-x
test/------rw-
test/------rwx
test/-----x---
test/-----x--x
test/-----x-w-
[12:23]jargon:find-test<271> find test -perm +g+w,u+r,a+x | wc -l
509

なんか数多すぎ..

[12:23]jargon:find-test<273> find test -perm +g+w | head
test
test/-------w-
test/-------wx
test/------rw-
test/------rwx
test/-----x-w-
test/-----x-wx
test/-----xrw-
test/-----xrwx
test/----w----

えー、g+wが立ってなくても出てるじゃん。なんだこれ??えらそうに書いてきた俺の立場にもなってくれ。

もしかして find utilsのバグか?? 以下の二つは同じ結果を返すって man findには書いてあるんだが:

[12:24]jargon:find-test<275> find test -perm +022 |wc -l
384
[12:24]jargon:find-test<276> find test -perm +g+w,o+w | wc -l
449

ちなみに versionはこれ:

[12:24]jargon:find-test<277> find --version
GNU find version 4.2.22
Features enabled: D_TYPE O_NOFOLLOW(enabled) 

まとめ

もう、よくわからなくなったのでクソして寝る! といいたいが俺は朝クソ派なので、こんな時間にクソできないのであった。困ったものだ。

May the shit be with you!

*1:あ、前提としてその下にあるファイルのオーナはすべて俺じゃなかったことを書いておく

*2:testディレクトリ自体も world readableなので出てきている