UNIX/Linux及びシェルにおいてファイルシステム内のファイルを検索する機能を利用しない日はないというほど、最も使用頻度が高いコマンドの1つとしてfind、更にファイルの中でもテキストファイルが重要な位置を占めるシステム環境において正規表現が利用できるファイル内検索機能を持つ使用頻度が高いコマンドの1つとしてgrepがあります。
findは、サブディレクトリを含め再帰的に(ドリルダウンして)ファイルを検索するコマンドでコマンドラインやシェルスクリプト(#!/usr/bin/shなど)内で利用されます。
findは、基本的にfind、検索開始位置となるディレクトリ、コマンドラインオプション、検索対象である固定文字列または正規表現パターンのファイル名指定、任意オプション(-print、-exec、-ok)を順にスペースを挟んで指定します。
$ find ../../src -name "*"
$ find / -user user_name
$ find . -group group_name
$ find /usr/local -type d
$ find . -executable
この例の-nameはカレントディレクトリ以下の全てのファイルを再帰的に検索するケースで「全て」を表す正規表現アスタリスク * を検索対象に指定、-userは指定したユーザーのファイルでこの例ではルートから検索、-groupは指定したグループ名に属するそのグループが作成管理するファイルを検索、この例ではカレントディレクトリ以下から検索、-typeはオプション引数としてb(block device)、d(directory)、f(file)、l(symbolic link)などファイル種別を取り指定されたタイプのファイルを検索、この例では/usr/local以下のディレクトリを検索、-executableは実行ファイル(-readableにすれば読み取り可能ファイル、-writableにすれば書き込み可能ファイル、併用すれば...)を検索、この例ではカレントディレクトリ以下の実行ファイルを検索するケースです。
$ find . -name "*" -print
$ find . -name "*.*" -exec grep "exe" { } \;
$ find . -name "[tT]???s" -ok grep "exe" { } \;
任意のオプションの内、findが既定で結果を標準出力に出力するのでその機能を持つ-printは基本的に省略可能、-exec、-okは共にfind結果に対してshellコマンドを実行するオプションであり、-okは実行時に確認メッセージを表示、何れも必ず中カッコ{ }、スペースに続けてバックスラッシュにセミコロン\;を付加します。
$ find . -name "[tT]???s" -ok grep "exe" \{ \} \;
尚、環境によっては、ファイル名に置き換わる波カッコの開始と終了にもバックスラッシュ\(日本語環境では円マーク)を付加する必要がある場合があります。
ちなみに環境によって制限がある場合もある-execの代わりにパイプとxargsを利用する方法があります。
$ ls find_test
ab1.sh find_finder1.sh
$ foo=finder
$ bar=find_test/
$ find $bar -name *$foo*.sh
find_test/find_finder1.sh
$ find "$bar" -name "*$foo*.sh"
find_test/find_finder1.sh
$ find $bar -name '*$foo*.sh'
$
$ find '$bar' -name "*$foo*.sh"
find: `$bar': No such file or directory
findコマンドは、検索開始パス指定においては変数などで展開を要する場合、クォートなしかダブルクォートを利用しないとシングルクォートでは変数展開されないのでUNIX/Linuxの多くのケースと同様、クォーテーションマークを利用する場合には、シングルクォートは固定文字列、それ以外の展開を要する変数などにはダブルクォートを利用します。
この例では、1つ下にfind_testというディレクトリににab1.sh、find_finder1.shというファイルがあるものとして、変数fooにファイル検索キーワードであるfinder、変数barに検索開始パスであるfind_testを格納し、検索開始パス、検索キーワードにおいてクォーテーションマークがどのように作用するかを表したもので、検索開始位置をシングルクォートで括った場合は、当該パスが存在しない旨のエラー、検索キーワードにおけるパターンをシングルクォートで括った場合は、該当するもの(ファイル「*$foo*.sh」)がないので結果なしとしてプロンプトだけが表示されます。
$ find . -name [a-c]?[0-9].sh
ab1.sh
$ find . -name ' [a-c]?[0-9].sh '
ab1.sh
$ find . -name " [a-c]?[0-9].sh "
ab1.sh
他方、この例のように-nameオプションで指定するファイル名の正規表現・パターンマッチではクォーテーションマークなし、または、シングルクォーテーション、ダブルクォーテーションで括った何れのケースでもカレントディレクトリ以下にあるa、b、cに、なんらかの1文字、更に0~9の数字1つが続く3文字から成るパターンの拡張子.shのファイルを再帰的に検索します。
findで正規表現及びパターンとして利用できるメタキャラクタは、ブラケット[ ]で括られたクラス、アスタリスク*、クエスチョンマーク(疑問符)?です。
メタ文字 | マッチ条件 |
---|---|
[ ] | 指定文字クラス |
* | 直前の文字やパターン0個以上 |
? | 何らかの1文字 |
ブラケット[ ]で括られたクラスは全てのアルファベット小文字[a-z]、全てのアルファベット大文字[A-Z]、全てのアラビア数字[0-9]や部分的に[a-c]や[5-7]という範囲指定、更には[Aa]bcのようにAbcまたはabcとマッチさせることもでき、逆にマッチさせたくない場合には[^Aa]とし、Aまたはaで始まらないbcにマッチさせることもできます。
[a-c]*は、a,b,c何れかが0回以上、*.htmlは拡張子.htmlのファイル(*.*とするとドットだけやドットで始まるファイルも含まれる)、???dfgは先行する何らかの3文字に続くdfg、?indとするとbind、find、kind、mind、windなどにマッチします。
grepは、入力リスト内検索やファイル内検索機能としてコマンドラインやシェルスクリプト(#!/usr/bin/shなど)内で利用されるコマンドでgrepの派生であるegrep、fgrepという別のコマンドもあり、以前は使い分けられていましたが、近年、例えばGNU grepでは-E(拡張正規表現)、-F(文字列固定正規表現)に加え-G/-Pオプションで基本正規表現/Perl正規表現なども利用することができるようになっています。
ちなみに入力リストにはls結果も含み、grepにls結果などのファイルリストを入力として渡せば、当然その中から条件にマッチするファイルを検索することも可能です。
grepコマンドは、基本的にgrepに続けてスペース、検索対象を文字列または正規表現パターン、検索対象のファイルの順に指定します。
$ grep abc foo*
$ grep "^[Gg]rep*" foo
$ find . -name "*" | xargs grep "word"
この例の1つめはfooで始まるファイル内で文字列abcを含む行を検索、2つめは先頭がGrepまたはgrepまたはそれらで始まる文字を含む行を検索、3つめはカレントディレクトリ以下のファイルを全て渡すfind結果をパイプ経由でxargsが受け取り、grepでファイル内にある文字列wordを検索する(ファイル名を記述する必要のない)ケースです。
$ cat foo
unix is os.
grep is unix command.
$ bar=e
$ grep gr${bar}p foo
unix is os.
$ grep "gr${bar}p" foo
unix is os.
$ grep 'gr${bar}p' foo
$
grepコマンドは、変数などで展開を要する場合、クォートなしかダブルクォートを利用しないとシングルクォートでは変数展開されないのでUNIX/Linuxの多くのケースと同様、クォーテーションマークを利用する場合には、シングルクォートは固定文字列、それ以外の展開を要する変数などにはダブルクォートを利用します。
この例では、かなり強引ですが、変数barに文字eを代入し、正規表現でgrepとなる文字を探すという想定($barpと解釈されないように文字pをクォートしてもいいのですが、クォートの例であるこのケースでは、クォートを二重にするとわかりにくいので他の方法として${bar}としていますが${bar}と$barが意味するところは同じ)で、クォーテーションマークがどのように作用するかを表したものですが、パターンをシングルクォートで括った場合は、該当するもの(文字列「gr${bar}p」)がないので結果なしとしてプロンプトだけが表示されます。
$ grep gr*p foo
$ grep "gr*p" foo
$ grep 'gr*p' foo
他方、この例の正規表現・パターンマッチにおいては、クォーテーションマークなし、または、シングルクォーテーション、ダブルクォーテーションで括った何れのケースでも文字列grepや他に該当する文字列があればマッチします。
この例では、ファイルfooに2つの文があり、grepで「gr」で始まり「0以上の何らかの文字」に続いて「p」で終わる文字を含むパターンにマッチするワードを検索しています。
grepで正規表現及びパターンとして利用できるメタキャラクタは、ブラケット[ ]で括られたクラス、アスタリスク*、ドット・ピリオド.、キャレット^、ドルマーク$、縦棒|、丸かっこ( )、バックスラッシュ\、バックスラッシュを伴う中カッコ\{\}です。
メタ文字 | マッチ条件 |
---|---|
[ ] | 指定文字クラス |
* | 直前の文字やパターン0個以上 |
. | 何らかの1文字 |
^ | 行先頭 |
$ | 行末尾 |
| | 条件or |
( ) | パターンクラス |
\ | バックスラッシュ (Windowsでは円マーク) |
\{n\} \{n,\} \{n,m\} | 指定出現回数 n n 回以上 n 回以上 m 回以下 ( n 及び m は整数) |
ブラケット[ ]と*はfindと同様、findの?(クエスチョンマーク・疑問符)は、grepでは.(ピリオド・ドット)で...dfg(ピリオド・ドット3つにdfg)は先行する何らかの3文字に続くdfg、^abc$(キャレットabcドル記号)は行先頭から行末尾にabcしかない(abcだけから成る)行、よって想像通り^$(キャレットとドル記号のみの組み合わせ)は空白行、abc|efg(abc縦棒efg)はabcまたはefg、丸かっことドット・ピリオド、アスタリスクの組み合わせのこのパターン(a.*)(..*)は、aとそれに続く0文字以上と、更に何らかの1文字以上が続くという2つのパターンを表すクラス、中カッコ{などの特殊文字の特殊性を無効にするエスケープの為のバックスラッシュ\{、3回の出現\{3\}、5回以上の出現\{5,\}、6回以上8回までの出現\{6,8\}のように使い、これ以外にもそれぞれ組み合わせて使うこともできます。
$ cat foo
$ grep "a_x" foo
$ grep "^[a]_x" foo
$ grep "[^a]_x" foo
尚、findの利用可能なメタ文字にキャレット^はありませんでしたが、grepで利用する場合、キャレット^は、使う位置によって2つの意味を持ち、角かっこ[ ]の外で使うと行先頭、内で使うと「~以外」のパターンを表します。
ちなみにgrepには角かっこ[ ]の内側でキャレット^を利用する場合と同等機能のマッチしないものを検索する-vオプションがあります。
$ grep -v "a_x" foo
$ grep -v "^[a]_x" foo
$ grep -v "[^a]_x" foo
同じキーワードの場合、-vオプションの有無によって検索キーワード指定の意味が逆になるので3つめの例では『「~以外」ではないもの』ということになり、-v 指定なしでgrepにシンプルにキーワードを渡すケースと同じ結果になります。
$ find . -name "*.txt" | xargs grep "pattern"
findはファイル検索、grepは入力リストやファイル内を検索するという性質から、「検索したファイルの中で、あるパターンを含むファイル」を探す為にパイプを使ってfind結果のファイル名を-exec/-okやxargsを通してgrepに渡すことは頻繁にありますし、更にパイプを通してその結果をawkに渡すケースも珍しくないでしょう。
$ ls -F
finder.html grepper.txt fine_grep.sh
$ cat grepper.txt
* file_name grepper.txt *
Chapter1 What's important ?
That is "find" and "grep" .
These command are very important!
...
$ find . -name "grep*.*" | xargs grep "That" | awk ' { print $3,$5 } '
"find" "grep"
尚、findやgrepの検索結果は標準出力に書き出されるので結果を残したい場合にはファイルリダイレクトを利用します。
ここでは、基本的なコマンドオプションや使用例に限定しましたが、これら以外についてはコマンドラインからfind --help、grep --help、更にmanやinfoで確認してください。