パーミッションを基準にしてfindをしたかった(FreeBSD)

パーミッションを基準にしてfindをしたかった。

(Linuxで同じように試したけれど、-permのオプションの挙動が違うのでアテにならない)

いままで幾度となくつまづいてきたfind。やっぱり便利なので使わざるを得ないのだけれど、今回もやはりつまづいてみた。

今回はファイルのパーミッションを変更したくて、ファイルの所有者にr, wが出ているファイルに対し、それぞれ同じようにグループに対してr, wを出したかった。

findに-execをつければよかんべ、と思ったのだけれど、ちょっと動作が思った通りに行かなくていろいろとやってみた。

まずググってみたのだけれど、とても有用な情報が出てきた。
find の -permを使う tips – ダイミテイ

このページにはperlのスクリプトがあり、このスクリプトで基本的なすべてのパーミッションを持つファイルを作成できる。
そのスクリプトをここにもパクって記載しておく。

やりたいこと

ファイルの所有者のパーミッションに沿ってgroupのパーミッションもかえてやる。
例えば以下のファイルの場合

-rw-------   1 mysql  mysql         235 Sep  5 00:10 relay-bin.000144

このファイルは所有者に対して読み取りと書き込みが出ている。このファイルの所有者はmysql、所属グループはmysqlとなっている。このとき、mysqlグループに属するユーザであれば、書き込みと読み込みを行わせたい。

また次のケースでは

drwx------   2 mysql  mysql        1536 Sep  2 05:06 search/

このファイル(ディレクトリ)は所有者に対して読み取り、書き込み、実行権限が出ている。このとき、mysqlグループに属するユーザに対しても同じようなパーミッションを出してやりたい。

上記二つの例では、それぞれ実行しようとするchmodコマンドが違う。なのでそれをうまいことやってやれるようなコマンドを探してみた。

まずはググって見つかったfindの良い例をするためのコード

#!/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, $_;
}

ここで作成されるファイルは全部で512個ある。owner, group, otherに対してそれぞれr, w, xがあるので
2 ^ (3*3) = 2 ^ 9 = 512個
となる。

さて、先述のリンク先の説明で一通りの理解はできたのだけれど、一つ引っかかるところがあった。

chmodは普段からよく使うので、知ったふりをして日々利用を続けてきたので、chmodなんて余裕、とか余裕をかましてた。

でもfindの-permオプションはちょっと違う。 find ./ -perm +a+wとかある。aの前に+がついてる。これがよくわからなかった。

-permオプションの引数について

まず、findの-permオプションをmanで調べてみる。

-perm [|+]mode The mode may be either symbolic (see chmod(1)) or an octal num- ber. If the mode is symbolic, a starting value of zero is assumed and the mode sets or clears permissions without regard to the process’ file mode creation mask. If the mode is octal, only bits 07777 (S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO) of the file’s mode bits participate in the comparison. If the mode is preceded by a dash (“-”), this primary evaluates to true if at least all of the bits in the mode are set in the file’s mode bits. If the mode is preceded by a plus (“+”), this primary evaluates to true if any of the bits in the mode are set in the file’s mode bits. Otherwise, this primary evaluates to true if the bits in the mode exactly match the file’s mode bits. Note, the first character of a symbolic mode may not be a dash (“-”).

find – FreeBSD man pages

また、-permのオプションについて、chmodを参考に呼び方を調べてみる。

mode ::= clause [, clause …]
clause ::= [who …] [action …] action
action ::= op [perm …]
who ::= a | u | g | o
op ::= + | – | =
perm ::= r | s | t | w | x | X | u | g | o
chmod – FreeBSD man pages

それぞれの式はclause(句)、また一文字で表されるものはsymbol(シンボル)と呼んでいるみたいだ。

ファイルのパーミッションの対象はwhoシンボル、とでも言えば良いらしい。

さて、findの-permオプションでは、whoシンボルとしてa(all, すべて), u(user, owner, ファイルの所有者), g(group, グループ), o(other, その他)の四種類がある。

先の参考リンクにもある例と同じことをやってみた

例1) -perm -a+r

shell> find ./ -perm -a+r
./r--r--r--
./r--r--r-x
./r--r--rw-
./r--r--rwx
./r--r-xr--
./r--r-xr-x
./r--r-xrw-
(後略)

shell> find ./ -perm -a+r | wc -l
      64

(補注: wcの結果は、./と./test.plを除いたもので-2してカウント)
上記、 -perm -a+r は、”すべてのwho句(シンボル?)に対してrがついているファイル” を対象とするらしい。

そしてもう一つ

例2) -perm +a+r

shell> find ./ -perm +a+r
./------r--
./------r-x
./------rw-
./------rwx
./-----xr--
./-----xr-x
./-----xrw-
(後略)
shell> find ./ -perm +a+r | wc -l
       448

(補注: wcの結果は、./と./test.plを除いたもので-2してカウント)
上記、 -perm +a+r は、 “任意のwho句(シンボル?)に対してrがついているファイル” を対象とするらしい。
言い換えると “whoシンボルのうちいずれかに対してrがついているファイル” ということらしい。

検証
rwxrwxrwxの文字列permに対し、それぞれの呼び方をs1 〜 s9とする。
例えばs5はgに対してのwであり、とりうる値はenumで(‘w’, null)であるとする。
例1)に対して検証する。
s1, s4, s7がとなるような文字列permの組み合わせは、s2, s3, s5, s6, s8, s9が任意となるので
2^6 = 64
これは 例1) -perm -a+rと合致する。

例2)に対して検証する。
s1, s4, s7のいずれかがrとなるような文字列permの組み合わせは、
s1, s4, s7のすべてがnullとなるような文字列permの組み合わせの余事象となる。
s1, s4, s7のすべてがnullとなるような文字列permの組み合わせは先の例と同様で
2^6 = 64 なので
例2な組み合わせは
512 – 64 = 448
これは 例2) -perm +a+rと合致する。

なので、 -permのあと、whoシンボルにつくオプション、+と-については、次のように読み替えるといいのではないかな。
+ => すべての
– => いずれかの
例えば、
+a+rであれば、すべてのwhoシンボルに対してrが出ているファイル
-a+rであれば、いずれかのwhoシンボルに対してrが出ているファイル
ということかな。

ちなみに、whoシンボルがaでないときは全く同じ結果になるみたい。
+u+rと-u+rは同じ結果を導く。
でもちなみに、
-a-rとか、-g-rとか、opシンボルが-な指定には、すべてのファイルが該当するみたい。

さて、ここまでやってきたのだけれど、なにか大きく困ったことがある。
それは以下の-permオプションが思ったように動作しない、ということ。

shell> find ./ -perm o-w | wc -l
1 (すべてのパーミッションが落ちているファイル、---------がヒット)
shell> find ./ -perm -o-w | wc -l
 512 (すべてのファイル)
shell> find ./ -perm +o-w | wc -l
 0 (該当なし)

上記はoに対してrのパーミッションが落ちているファイル、を指定したいのだけれど、いずれもそのようには動作しない。

なんで動かないのか、というのは、permシンボルにたいして、opシンボルが-のものは受け付けない、ということなんだろう。
つまり、あるパーミッションが落ちているファイル、というのは、find側の条件で、 ! -perm +o+w のように書けばよいみたい。
ここで注意する点があって、
otherのwが落ちているファイル、というのは、 ! -perm o+w ではなく、 ! -perm +o+w である、ということ。
! -perm o+w であれば、otherのwのみが立っているファイル以外、つまり002以外のファイルを対象としてしまう。
! -perm +o+w であれば、otherのwが立っていて、ほかのパーミッションは何れかで良いファイル、を対象とする。今回はこれが欲しい。

ということで結論として
groupのwなどのパーミッションが落ちているファイルのパーミッションを所有者に沿って上げる場合、以下の三つを実行すれば良い。

shell> find ./ ! -perm +g+r -perm +u+r -exec sudo chmod g+r {} \;
shell> find ./ ! -perm +g+w -perm +u+w -exec sudo chmod g+w {} \;
shell> find ./ ! -perm +g+x -perm +u+x -exec sudo chmod g+x {} \;

注意事項として、びっくりマークと-permの間にはスペースを空けること、そうでなければシェルが!を解釈しようとして過去のヒストリから参照しようとしたりする。(at tcsh)
中括弧とバックスラッシュセミコロンの間にスペースを空けること、これはfindの-execに送る決まった書き方で、ここのスペースがないとうまく解釈できない。

そして何やら、chmodにパーミッションをほかのwhoシンボルに似せる、とかありそう•••

コメントを残す