find -exec と find | xargs との違い

findにおける-execとxargsの違いの解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、find コマンド実行時によく利用される -exec と | xargs との違いについて解説していきます。

-exec に関してはコマンドの終端文字が \; の場合と + とで動作が異なりますので、この違いについても解説していきます。

つまり、このページでは下記の3つの違いについて解説していきます。

  • -exec コマンド {} \;
  • -exec コマンド {} +
  • | xargs コマンド

これらを利用することで、find で検索された全ファイルに対して -exec| xargs の後ろに指定したコマンドを実行することができるようになります。

例えば、下記の3つは全て、find で検索された拡張子 .txt のファイルに rm を実行するコマンドとなります。rm 部分に別のコマンドを指定すれば、検索されたファイルに対し、その指定したコマンドが実行されるようになります。

-exec {}\;の使用例
% find . -name "*.txt" -exec rm {} \;
-exec {}+の使用例
% find . -name "*.txt" -exec rm {} +
xargsの使用例
% find . -name "*.txt" | xargs rm

特別なケースを除いて、最終的な結果としては3つとも同じになります(特別なケースについては後述で解説します)。

ただし、それでも上記の3つではコマンド実行時の動作が異なります。

では、どのように動作が異なるのか?この点について解説していきます。

実例で違いを確認

まずは、これらの違いを理解していただくために、下記の3つを実行した際の動作の違いを実例で確認していきたいと思います。

  • -exec コマンド {} \;
  • -exec コマンド {} +
  • | xargs コマンド

今回は、下記の find コマンドを実行した結果、

% find . -name "*.txt"

下記の4つのファイルが検索結果として見つけられるようなファイル構成においての、各コマンドの動作の違いについて確認していきたいと思います。

  • ./test1.txt
  • ./test2.txt
  • ./test3.txt
  • ./te st4.txt

つまり、カレントディレクトリに上記の4つのファイルが存在するというわけですね!

また、4つ目のファイルパスの途中にスペースがありますが、これはわざとです。

では、このようなファイル構成において下記の3つをそれぞれ実行し、find で見つけたファイル全てに対して rm コマンドを実行する際の動作について確認していきましょう。

  • -exec コマンド {} \;
  • -exec コマンド {} +
  • | xargs コマンド

-exec コマンド {} \; の使用例

まずは -exec コマンド {} \; を利用して下記のコマンドを実行した時の動作について確認していきたいと思います。

-exec \;
% find . -name "*.txt" -exec rm {} \;

この場合、実際には rm コマンドは下記のように find コマンドで見つけたファイルの数の分だけ実行されることになります。

% rm "./test1.txt"
% rm "./test2.txt"
% rm "./test3.txt" 
% rm "./te st4.txt"

スポンサーリンク

-exec コマンド {} + の使用例

それに対し、-exec コマンド {} + を利用して下記のコマンドを実行した場合、

-exec {}+
% find . -name "*.txt" -exec rm {} +

イメージとしては下記のように rm が実行されることになります。つまり、find コマンドで見つけたファイルのパスを並べて引数として指定して rm コマンドが1度だけ実行されることになります。

% rm "./test1.txt" "./test2.txt" "./test3.txt" "./te st4.txt"

| xargs の使用例

さらに、| xargs を利用して下記のコマンドを実行した場合、

xargsの使用例
% find . -name "*.txt" | xargs rm

下記のように rm が実行されることになります。つまり、find コマンドで見つけたファイルを並べて引数として指定して rm コマンドが1度だけ実行されることになります。

% rm ./test1.txt ./test2.txt ./test3.txt ./te st4.txt

ただし、xargs の場合、./te st4.txt の空白を区切りに別々の引数として rm が実行されることになります。

つまり、上記のコマンドでは ./test4.txt に対して rm コマンドが実行されることになります(実際にはそのようなファイルは存在しないのでエラーが発生することになるでしょう)。

簡単な例でしたが、下記の3つのコマンドの違いが何となく理解できたのではないでしょうか?

続いては、下記の3つの動作の詳細を個別に説明していきたいと思います。

  • | xargs コマンド
  • -exec コマンド {} \;
  • -exec コマンド {} +

-exec コマンド {} \;

実例で違いを確認 で紹介した例からも分かるように、-exec コマンド {} \; を利用した場合、find コマンドで見つけられた1つのファイルのパスに対して、-exec の後ろに指定したコマンドが1回実行されることになります。

execコマンド{};でのコマンドの実行のされ方の例

つまり、find コマンドで見つけられたファイルの数の分だけ -exec の後ろ側のコマンドが実行されることになります。そしてこの時、find コマンドで見つけられたファイルのパスが、その後ろ側のコマンドの引数に指定されることになります。

スポンサーリンク

処理は低速

find コマンドで見つけたファイルの数だけ -exec の後ろに指定したコマンドが繰り返し実行されることになるため、-exec コマンド {} \; は遅いです。

コマンドを実行する際には、まずプログラムを起動するという処理が行われることになります。そして、このプログラムの起動には多少ではあるものの時間がかかります。find コマンドでファイルが見つけられる度にプログラムの起動が行われる -exec コマンド {} \; は、特に find コマンドで見つけられるファイルの数が多い場合は処理時間が長くなってしまいます。

例えば下記のコマンドで 2000 個のファイルに対して rm が実行されるとした時(つまり find コマンドで 2000 個のファイルが見つけられる)、私の環境では下記の実行にかかる時間は約 6.8 秒でした。

% find . -name "*.txt" -exec rm {} \;

後述で説明するように、-exec コマンド {} + の場合は実行にかかる時間は 0.3 秒でした。

もちろん、実行する環境によって必要となる処理時間は変わると思いますが、上記の結果から、-exec コマンド {} \; の遅さを理解していただけるのではないかと思います。

-exec コマンド {} +

-exec コマンド {} \; でファイル1つ1つに対して毎回 -exec の後ろ側のコマンドが実行されるのに対し、-exec コマンド {} + を利用した場合は find コマンドで見つけたファイルを一度にまとめて -exec の後ろ側のコマンドの引数に指定して実行されることになります。

「execコマンド{}+」でのコマンドの実行のされ方の例

ここまでの例で使用してきた rm だけでなく、一般的な Linux や Mac のコマンドではコマンドライン引数の個数が複数でも受け付けられるように開発されているので、exec コマンド {} + で複数の引数に対して一括してコマンドを実行させることができるようになっています。

MEMO

一般的なコマンドだけでなく、複数の引数を受け付けられるようにプログラムやスクリプトを自作すれば、それらと exec コマンド {} + を併用して検索したファイル全てに対して処理を実行させられるようにもなります

ただし、コマンドに指定可能な引数の個数には上限があります。find コマンドで見つけたファイルが多すぎて引数に指定可能な個数の上限を超えるような場合は、エラーにならないように複数回に自動的に分けられて -exec の後ろ側のコマンドが実行されることになります。このような場合は -exec の後ろ側のコマンドの実行回数が複数回になることになりますが、それでも -exec コマンド {} \; に比べれば実行回数は少なくなります。

処理は高速

ということで、-exec の後ろ側のコマンドが実行される回数は -exec コマンド {} \; を利用した時よりも少なくなり、その分 -exec コマンド {} + の方が処理が高速になります。

例えば下記のコマンドで 2000 個のファイルに対して rm が実行されるとした時、私の環境では下記を実行するのにかかる時間は約 0.3 秒でした。

% find . -name "*.txt" -exec rm {} +

-exec コマンド {} \; でも説明したように、-exec コマンド {} \; の場合は実行にかかる時間は 6.8 秒でしたので、exec コマンド {} + の方が断然高速ですね!

特に find で見つけられるファイル数が多い場合は -exec コマンド {} \; よりも exec コマンド {} + を積極的に利用する方が良いです。

スポンサーリンク

| xargs コマンド

最後に | xargs コマンド について解説していきます。

-exec コマンド {} + と似ているが空白文字等の扱いに注意 

| xargs コマンド はコマンドの実行の仕方や引数の指定の仕方は -exec コマンド {} + と似ています。

| xargs コマンド を利用した場合、-exec コマンド {} + 同様に find コマンドで見つけたファイルをまとめて一度に | xargs の後ろ側のコマンドの引数に指定して実行されることになります。

そのため、-exec コマンド {} + と同様に | xargs コマンド の処理は高速です。

ただ、似ているものの違いはあります。

それは、| xargs では、デフォルトでは空白文字(スペースや改行など)を引数の区切りとして使用するという点になります。

そのため、find コマンドで見つけたファイルのパスに空白文字が含まれる場合、空白文字を区切りとして異なる引数として | xargs の後ろ側のコマンドに指定されることになります。

「|xargs コマンド」でのコマンドの実行のされ方の例

それに対し、-exec コマンド {} + の場合、必ず find コマンドで見つけたファイルのパスがそのまま1つの引数として指定されます。なので、パスに空白文字等の区切り文字が含まれていたとしても、1つのパスとして -exec の後ろ側のコマンドに引数として指定されることになります。

ただ、| xargs でもオプションを利用することで、-exec コマンド {} + 同様にパスの中に空白文字等がある場合も1つの引数として扱うことが可能になります。具体的には、find コマンドに -print0 オプションを、さらに xargs コマンドに -0 オプションを指定することで、パスの中に区切り文字等があった場合も1つの引数として扱うことが可能になります。

% find . -name "**.txt" -print0 | xargs -0 コマンド

xargs-0 オプションを指定することで、xargs では入力された文字列をヌル文字(下の図における \0)という特殊な文字を区切りに1つの引数として扱うようになります。

xargsに-0オプションを付与する効果を説明する図

さらに、find-print0 オプションを指定することで、find は見つけたファイルの各パスを改行ではなくヌル文字区切りで出力するようになります。

findに-print0オプションを付与する効果を説明する図

これにより、パスの中に空白文字が存在した場合でも、xargs ではその空白文字を区切り文字ではなく単なる文字として扱い、ヌル文字が現れるまでを1つの引数として扱うようになります。

このため、find が見つけたファイルのパスをそのまま xargs の後ろ側のコマンドに引数として指定することができるようになります。

xargs は汎用性が高い

ここまで -exec| xargs との比較を行なってきましたが、実はこれらは全く種類の異なるものです。

まず、-execfind のオプションの1つです。そのため -exec は他のコマンドと併用できない場合も多く、find 以外ではほとんど使い道のないものと言えると思います。

それに対して xargs は、find 同様にコマンドの1つとなります。xargs をパイプ | と併用することで | xargs の前側のコマンドの出力結果を | xargs の後ろ側のコマンドの引数に変換し、複数のコマンドを連携して動作させることができます。

この | xargs に関しては find 以外のコマンドとも併用可能であり、使い所は -exec よりも多いと思います。そして、複数のコマンドを連携して動作させるために非常に重要なコマンドですので、| xargs に関しては必ず覚えておいた方が良いと思います。

| xargs やパイプ | については下記ページで解説していますので、詳しく知りたい方はぜひ下記ページを読んでみてください。

パイプの解説ページアイキャッチ パイプ | について解説( | と | xargs の違いも理解できる!)

スポンサーリンク

-exec {} \;-exec {} +| xargs の違い

最後に -exec {} \;-exec {} +| xargs の3つの違いについてまとめておきます。

コマンドの実行の仕方

find で見つけたファイルに対し、-exec {} \;-exec {} +| xargs それぞれのコマンドの実行の仕方は下記のようになります。

  • -exec {} \;:1つのファイルに対して1回実行
  • -exec {} +:複数のファイルに対して1回実行
  • | xargs:複数のファイルに対して1回実行

後者の2つに関しては、引数の個数が上限以下となるようにコマンドの実行回数が自動的に変化します(引数の個数が上限を越えない場合は全てのファイルに対して1回のみ実行される)。

特徴

-exec {} \;-exec {} +| xargs それぞれの特徴もまとめておきます。

  • -exec {} \;:遅い
  • -exec {} +:速い
  • | xargs:速い・空白文字の扱いに注意・他のコマンドにも併用可能

スポンサーリンク

まとめ

このページでは、find とよく併用される下記の3つについて解説しました!

これらの3つでは -exec| xargs に指定したコマンドの実行の仕方・引数の指定の仕方が異なり、それにより処理速度が異なります。

  • -exec コマンド {} \;
  • -exec コマンド {} +
  • | xargs コマンド

find と併用するのであれば、とりあえず一番無難なのは -exec コマンド {} + だと思います。

また、| xargs に関しては find だけでなく、他のコマンドとの連携を実現するための重要なコマンドですので、使い方は是非覚えておきましょう!

一番大事なのは、上記の3つにおける「コマンドの実行の仕方」や「コマンドへの引数の渡し方」の違いをしっかり理解しておくことだと思います。

この違いを理解しておけば、例えば下記のような問題や要望が発生した際に、自然と解決策も思いつくようになると思います。

  • コマンドの実行に時間がかかりすぎる
  • パスにスペースが含まれていてうまく動作しない
  • find 以外でも、コマンドの実行結果を別のコマンドの引数に指定したい

なので、是非これらの3つの違いについてしっかり理解しておき、問題等が発生した際に解決できるようにしておきましょう!

同じカテゴリのページ一覧を表示

1 COMMENT

baka

厳密なコマンドを作ることをわかっていない、クソ記事、特に以下
—–
また、これも -exec コマンド {} \; と裏返しの話になりますが、-exec コマンド {} + の場合は -exec の後ろ側のコマンドに指定される引数の個数は find で見つけられるファイルの数によって変わります。

そのため、「引数の個数が固定である」ことを前提として作られたコマンドやプログラムを -exec に指定すると意図通りに動作しない可能性が高いので注意してください。

ただ、Linux や Mac にあらかじめ用意されているコマンドに関しては、ほとんどのものは引数の個数が可変となるように作られていると思います。なので、そこまで気にするほどのデメリットにはならないのではないかと思います。

返信する

baka へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です