@legitwhiz

legit whiz 技術レポート

View on GitHub

findコマンドで-execオプションを使用する時の最後の「{} ;」ってなんだっけ?

私は、-execじゃなく殆どxargsを使ってますが、シェル改修で-execを久々に見ました。 過去、-execオプションも使ってましたが最後の「{} ;」ってなんだっけ? と素朴に思いました。 まあ、manを見ろって話なんですが。

findのman

-exec command ;
>すべてコマンドに対する引き数と見なされる。文字列 `{}` は、
>それがコマンドの引き数中に現れるすべての場所で、現在処理中のファイル名に
>置き換えられる。

パイプ(“|”) 処理における展開(“-“) を find 内でいうと “{}” になるってことなんですかね。

manを更に読み進めると「-exec {} +」ってのもあったんですね。

>選択したファイルに対して指定したコマンドを実行するが、
>コマンドラインを形成するとき、選択した各ファイル名をコマンドラインの末尾に
>追加して行くという方法を取る

「-exec {} ;」と「-exec {} +」の違いは、ググったら

「-exec {} +」はファイルをまとめて実行する(グループ実行) 全ファイルのパスに置き換えられる。 「-exec {} ;」はファイルを1つずつ実行する(単体実行) ファイルパスにならない

ただし、-exec {} ; より xargs が推奨みたいですね。 理由は、find の -exec では 1つのファイルに対して 1回コマンドを実行するが、 xargs ならカーネルが許す限り長いコマンドを作って実行するため、 -exec より xargs の方が fork&exec の回数が少なくなって効率的なはずだそうです。

実際ファイル数が数万個ともなると実行時間に差異が出てくるので分かると思います。

find比較

execとxargsとの比較

time find /var -type f -exec ls -l {} ; > /dev/null
real    0m23.684s
user    0m6.679s
sys     0m15.602s

time find /var -type f | xargs ls -l > /dev/null
real    0m0.441s
user    0m0.345s
sys     0m0.160s

キャッシュによる影響確認

明らかにxargsの方が早いですね。 ちなみにキャッシュの関係性もあるかもしれないので キャッシュをクリア。

[root@server ~]# sync && sysctl -w vm.drop_caches=3
[root@server ~]# free
             total       used       free     shared    buffers     cached
Mem:       1034564    1003368      31196          0     123764     761776
-/+ buffers/cache:     117828     916736
Swap:      2031608     107724    1923884
# sync
# echo 3 > /proc/sys/vm/drop_caches
# free
             total       used       free     shared    buffers     cached
Mem:       1034564     240196     794368          0        664     150404
-/+ buffers/cache:      89128     945436
Swap:      2031608     107724    1923884

キャッシュをクリアして何度か実施しましたが ほぼ、変わらず。

[root@server ~]# time find /var -type f | xargs ls -l  > /dev/null

real    0m0.526s
user    0m0.344s
sys     0m0.182s
[root@server ~]# time find /var -type f | xargs ls -l  > /dev/null

real    0m0.443s
user    0m0.326s
sys     0m0.180s

findに[-ls]オプションでは?

ちなみにLinuxではfindに[-ls]というオプションがあるので 同様に計測してみましたが、xargsとほぼ同じ性能でした。

[root@server ~]# time find /var -type f -ls > /dev/null

real    0m0.914s
user    0m0.237s
sys     0m0.280s

更にキャッシュクリアしても、あまり差はないですね。

[root@server ~]# time find /var -type f -ls > /dev/null

real    0m0.154s
user    0m0.100s
sys     0m0.054s

まあ、スクリプトでそんなにcpやmvすることはあっても 大量にlsすることはないんでしょうけど。