Subject: Why doesn't find's "{}" symbol do what I want?
Date: Thu Mar 18 17:16:55 EST 1993

3.4) find の "{}" が思うように動かないのですが。

"find" には、選択された全ファイルに対して指定したコマンドを実行する -exec オプションがあります。find は "{}" をその時点で検索されたファイル の名前に置き換えます。

さて、ある日あなたは "find" を使って、各ディレクトリの下の全ファイルに ある command を実行しようとしました。あなたはこうするかもしれません。

find /path -type d -exec command {}/¥* ¥;
すると find は次のように、各ディレクトリに対して順に command を実行する と思うでしょう。

    
command directory1/*
command directory2/*
...
残念ながら、find によって "{}" 記号が展開されるのは、 "{}" 記号が独立し て使用される場合だけです。他の用法、例えば "{}/*" などの場合、"{}" は展 開されずにそのまま残ってしまいます。従って、上の例の find の使用例では 予想どうりの動作はしないで、それぞれのディレクトリに対して
    
command {}/*
command {}/*
...
を実行することになります。これが find のバグであれ決められた仕様であ れ、find がこのように動作する事実は変わりません。

さて、ではこの問題にどう対処したらいいでしょう。方法のひとつは、

command "$1"/*
という 1 行の小さなシェルスクリプトを作り(これを "./doit" と名付けたと して)、

find /path -type d -exec ./doit {} ¥;
とすることです。シェルスクリプト "./doit" を使いたくなかったら、
find /path -type d -exec sh -c 'command $0/*' {} ¥;
としても同じです("sh -c 'command' A B C" とすると、'command' の内部で は $0 は A、$1 は B、... に展開されます。上の例では、$0 が選択された ディレクトリの名前に展開されるわけです)。

sed を使って

find /path -type d -print | sed 's:.*:command &/*:' | sh
というようにコマンドラインを sed で作ってしまう方法もあります。

"command" が呼び出される回数を少なくしたいということであれば、xargs コ マンドが使えるかどうか調べてみましょう。xargs は "command" に与える引数 を次々と標準入力から読み込み、1行のコマンドラインに詰め込めるだけ詰め 込んで "command" を呼び出します。従って、

find /path -print | xargs command
とすると
command file1 file2 file3 file4 dir1/file1 dir1/file2
が実行されるわけです(ファイルの数が多くて1行に納まらない場合は何回かに 分けて "command" が実行されます) 。

しかしこれは完全に確実で安全な解決法ではありません。xargs が標準入力か ら読み込む引数は改行記号で区切られることになっており、改行記号のような 特殊記号を含む名前を持つファイルに対しては動作が不安定になるので注意が 必要です。


UNIX FAQ LIST / Copyright(c)1994,Ted Timar / tmatimar@isgtec.com


Maintainer: あさだ たくや