Linux で find コマンドを使用する方法

  • Linux の find コマンドは、ファイルの種類や所有者など、さまざまな基準でファイルやディレクトリを検索するための強力なツールです。
  • find コマンドを xargs とともに使用することで、検索結果を他のコマンドに渡してさらに処理できます。
  • find コマンドの -exec オプションを使用すると、外部プログラムを呼び出して、検索によって返されたファイル名に対して追加の処理を実行できます。

Linux のfindコマンドは、ファイルやディレクトリを検索するのに優れています。しかし、検索結果を他のプログラムに渡してさらに処理することもできます。その方法をご紹介します。

Linux の find コマンド

Linux のfindコマンドは、強力で柔軟です。ファイル名だけでなく、さまざまな基準を使用してファイルやディレクトリを検索できます。たとえば、空のファイル、実行可能ファイル、または特定のユーザーが所有するファイルなどを検索できます。アクセス時間や変更時間によってファイルを見つけて一覧表示したり、正規表現パターンを使用したり、デフォルトで再帰的に動作したり、名前付きパイプ (FIFO バッファー) などの擬似ファイルで動作したりできます。

これらはすべて非常に役立ちます。控えめなfindコマンドは、本当に強力です。しかし、そのパワーを活用して、さらにレベルアップする方法があります。findコマンドの出力を取得して、他のコマンドの入力として自動的に使用できれば、find で見つけたファイルやディレクトリに対して何かを実行できます。

1 つのコマンドの出力を別のコマンドにパイプで渡すという原則は、Unix 系オペレーティング システムの中核的な特徴です。プログラムに 1 つのことをうまく実行させ、その出力が別のプログラム (まだ作成されていないプログラムでも) の入力になることを期待するという設計原則は、多くの場合「Unix の哲学」と呼ばれています。それでも、mkdir のように、パイプで渡された入力を受け入れないコア ユーティリティもあります。

この欠点を解決するために、xargsコマンドを使用して、パイプで渡された入力を小分けにして、それらをコマンドライン パラメーターであるかのように他のコマンドに渡すことができます。これにより、単純なパイプとほぼ同じことが実現します。それは「ほぼ同じ」であり、「まったく同じ」ではありません。シェル展開やファイル名グロビングで予期しない違いが生じる可能性があるためです。

Linux の find コマンドの例をいくつか紹介します。その用途の一部を説明します。

find を xargs とともに使用

findxargsを使用して、見つかったファイルに対して実行されるアクションを使用できます。これは遠回りな方法ですが、findで見つかったファイルをxargsに渡して、それらをパイプでtarに渡し、それらのファイルのアーカイブ ファイルを作成できます。このコマンドは、多数のヘルプ システム PAGE ファイルがあるディレクトリで実行します。

find ./ -name "*.page" -type f -print0 | xargs -0 tar -cvzf page_files.tar.gz

このコマンドは、さまざまな要素で構成されています。

  • find ./ -name "*.page" -type f -print0: find アクションは現在のディレクトリで開始され、"* .page" 検索文字列に一致するファイルの名前で検索されます。-type fを使用してファイルのみを検索するように特別に指示されているため、ディレクトリは一覧表示されません。print0引数は、findに空白をファイル名の終わりとして扱わないように指示します。つまり、ファイル名にスペースが含まれている場合でも、正しく処理されます。
  • xargs -o: -0引数はxargsに空白をファイル名の終わりとして扱わないように指示します。
  • tar -cvzf page_files.tar.gz: これは、xargsfindからファイル リストを渡すコマンドです。tar ユーティリティは "page_files.tar.gz" という名前のアーカイブ ファイルを作成します。

lsを使用して、作成されたアーカイブ ファイルを確認できます。

ls *.gz

アーカイブ ファイルが作成されます。これが機能するためには、すべてのファイル名をtarにまとめて渡す必要があります。それが起こりました。すべてのファイル名が、非常に長いコマンド ラインとしてtarコマンドの最後にタグ付けされました。

最後のコマンドをすべてのファイル名で一度に実行するか、ファイル名ごとに一度呼び出すかを選択できます。xargsからの出力を、行と文字をカウントするユーティリティwcにパイプすることで、その違いを簡単に確認できます。

このコマンドは、すべてのファイル名を一度にwcにパイプします。事実上、xargsは、それぞれのファイル名が含まれるwcの長いコマンド ラインを構築します。

find . -name "*.page" -type f -print0 | xargs -0 wc

各ファイルの行、単語、文字が、すべてのファイルの合計とともに印刷されます。

xarg-I (文字列を置換) オプションを使用し、置換文字列トークン (この場合は "{}") を定義すると、トークンは最終的なコマンドで各ファイル名に順番に置き換えられます。これは、wcが各ファイルに対して 1 回ずつ、繰り返し呼び出されることを意味します。

find . -name "*.page" -type f -print0 | xargs -0 -I "{}" wc "{}"

出力がきれいに整列されていません。wcの各呼び出しは単一のファイルで動作するため、wcは出力を整列させるものを持っていません。出力の各行は独立したテキスト行です。

wcは複数のファイルで一度に処理した場合にのみ合計を提供できるため、要約統計は得られません。

find の -exec オプション

findコマンドには、返されるファイル名に対してさらに処理を実行するために外部プログラムを呼び出す組み込みメソッドがあります。-exec (実行) オプションの構文は、xargsコマンドに似ていますが異なります。

find . -name "*.page" -type f -exec wc -c "{}" \;

これにより、一致するファイルの単語がカウントされます。このコマンドは、以下の要素で構成されています。

  • find .: 現在のディレクトリで検索を開始します。findコマンドはデフォルトで再帰的であるため、サブディレクトリも検索されます。
  • -name "*.page": "*.page" 検索文字列に一致する名前を持つファイルを探しています。
  • -type f: ディレクトリではなく、ファイルのみを検索しています。
  • -exec wc: 検索文字列と一致したファイル名に対してwcコマンドを実行します。
  • -w: コマンドに渡したいオプションは、コマンドの直後に配置する必要があります。
  • "{}": "{}" プレースホルダーは各ファイル名を表し、パラメータ リストの最後の項目である必要があります。
  • \;: セミコロン ";" は、パラメータ リストの終わりを示すために使用されます。シェルが解釈しないように、バックスラッシュ "\" でエスケープする必要があります。

そのコマンドを実行すると、wcの出力が表示されます。-c (バイト数) は、出力を各ファイルのバイト数に制限します。

ご覧のとおり、合計はありません。wcコマンドはファイル名ごとに 1 回実行されます。終了セミコロン ";" をプラス記号 "+" に置き換えることで、-execの動作を一度にすべてのファイルで動作するように変更できます。

find . -name "*.page" -type f -exec wc -c "{}" \+

すべてのファイルが 1 つの長いコマンドラインとしてwcに渡されたことを示す、要約合計ときれいに表形式でまとめられた結果が得られます。

exec は本当に exec を意味する

-exec (実行) オプションは、現在のシェルで実行してコマンドを起動しません。Linux の組み込み exec を使用してコマンドを実行し、現在のプロセス (シェル) をコマンドに置き換えます。そのため、起動されたコマンドはシェルで実行されていません。シェルがなければ、ワイルドカードのシェル展開を取得できず、エイリアスやシェル関数にアクセスできません。

このコンピューターには、words-onlyというシェル関数が定義されています。これは、ファイル内の単語のみをカウントします。

function words-only () { wc -w $1}

おそらく奇妙な関数である "words-only" は "wc -w" よりもはるかに長く入力する必要がありますが、少なくともwcのコマンドライン オプションを覚える必要はありません。次のようにして、それが何をするかをテストできます。

words-only user_commands.pages

通常のコマンドライン呼び出しでは正常に機能します。find-execオプションを使用してその関数を呼び出そうとすると、失敗します。

find . -name "*.page" -type f -exec words-only "{}" \;

findコマンドはシェル関数を見つけることができず、-execアクションは失敗します。

これを克服するには、findで Bash シェルを起動し、コマンドラインの残りの部分をシェルへの引数として渡します。コマンドラインを二重引用符で囲む必要があります。つまり、"{}" 置換文字列を囲む二重引用符をエスケープする必要があります。

findコマンドを実行する前に、-f (関数として) オプションを使用してシェル関数をエクスポートする必要があります。

export -f words-only

find . -name "*.page" -type f -exec bash -c "words-only \"{}\"" \;

これは期待通りに実行されます。

Find でファイル名を複数回使用する

複数のコマンドをチェーンでつなげたい場合は、そうすることができ、各コマンドで "{}" 置換文字列を使用できます。

find . -name "*.page" -type f -exec bash -c "basename "{}" && words-only "{}"" \;

「pages」ディレクトリから 1 レベル上までcdしてそのコマンドを実行すると、findは再帰的に検索するため、PAGE ファイルを検出します。ファイル名とパスは、以前と同様にwords-only関数に渡されます。2 つのコマンドで-execの使用を実証する目的のみで、basenameコマンドも呼び出して、パスのないファイル名を表示しています。

basenameコマンドとwords-onlyシェル関数の両方で、ファイル名は "{}" 置換文字列を使用して渡されます。

状況に応じた使い分け

一度呼び出してすべてのファイル名を一度に渡すことができれば、コマンドを繰り返し呼び出すことによる CPU 負荷と時間のペナルティを回避できます。そして、コマンドを起動するために毎回新しいシェルを呼び出していると、そのオーバーヘッドはさらに悪化します。

しかし、場合によっては (実現しようとしている内容によって異なりますが) 他の選択肢がない場合があります。状況に応じてどのような方法が必要であっても、Linux は特定のニーズに合った方法を見つけるのに十分なオプションを提供しているため、誰もが驚くことはないでしょう。