ファイル名拡張の場合、「find」ユーティリティの「-name」オプションは同様に機能するように見えますが、bashシェルの組み込みパターンの一致とまったく同じではありません。
以下はGNUリファレンスマニュアルの関連部分です:
- Bashシェルパターンマッチング:http://www.gnu.org/software/bash/manual/bashref.html#Pattern-Matching
- ユーティリティパターンの一致を探す:http://www.gnu.org/software/findutils/manual/html_mono/find.html#Shell-Pattern-Matching
これはそれ自体とても混乱しています。混乱を加えるために、「find」ユーティリティのマニュアルページ(上記参照)のセクション2.1.4のタイトルは「Shell Pattern Matching」です。これは、「find」がシェルに組み込まれたパターンマッチング機能を使用していることを意味します。ただし、Findのマニュアルページ(http://goo.gl/ngQTKx)、「-nameモード」の内容は次のとおりです。
「ファイル名の一致はfnmatch(3)ライブラリ関数を使用して行われます。パターンがシェルによって拡張されるのを防ぐために、パターンを引用符で囲むことを忘れないでください。」
これからパターンマッチングを行うのは、シェルではなくfnmatchライブラリのfindユーティリティであるようです。
私の質問は次のとおりです。
- bashシェルのデフォルトのファイル名拡張子とパターンマッチング(extglobシェルオプションを無効にする)は、-nameオプションを使用するfindユーティリティとは異なりますか?
- それでは、これらの違いは何ですか?
- bashはファイル名拡張とパターンマッチングのためにfnmatchライブラリや他のメカニズムを使用しますか?
答え1
シェルでは区別する必要がありますファイル名の作成/拡張(別名ワイルドカード:ファイルリストに展開されたパターン)パターンマッチング。ワイルドカード使用内部パターンマッチングしかし、実際には最初にオペレータ生産するファイルリストベース模様。
*/*.txt
は模様0個以上の文字シーケンスと一致し、その後に/
0個以上の文字シーケンスが続き、その後に続きます.txt
。シェルモードで使用すると、次のようになります。
case $file in
*/*.txt) echo match
esac
一致しますfile=.foo/bar/baz.txt
。
*/*.txt
しかし、全体的な状況関連していますが、もっと複雑です。
*/*.txt
ファイルリストに展開すると、シェルは現在のディレクトリを開き、その内容を一覧表示し、ディレクトリの種類(またはディレクトリへのシンボリックリンク)に一致する隠しファイルを見つけ、リストを並べ替え、*
各ディレクトリを開き、内容を一覧表示し、隠されていないものと一致します*.txt
。
パターンと一致しても動作しないため、.foo/bar/bar.txt
絶対に拡張されません。一方、生成されたファイルパスは次のとおりです。全体的な状況このパターンと一致します。
foo[a/b]baz*
同様に、glob likeは名前で始まるディレクトリ内のすべてのファイルを探します。b]baz
foo[a
したがって、パターンマッチングではなくワイルドカードの場合は/
特別であり(ワイルドカードは何らかの方法で分割され、/
各部分は別々に処理されます)、ドットファイルが特別に処理されることを確認しました。
シェルワイルドカードとパターンマッチングはシェル構文の一部です。これは、引用やその他の形式の拡張と絡み合っています。
$ bash -c 'case "]" in [x"]"]) echo true; esac'
true
引用すると、]
特別な意味が削除されます(以前の意味は閉じられます[
)。
すべてを混ぜるともっと複雑になります。
$ ls
* \* \a x
$ p='\*' ksh -xc 'ls $p'
+ ls '\*' '\a'
\* \a
OK\*
はすべてです\
。
$ p='\*' bash -xc 'ls $p'
+ ls '\*'
\*
すべてがで始まるわけではありません\
。したがって、\
エスケープされたことは間違い*
ありませんが、再一致しません*
。
検索の場合ははるかに簡単です。find
受け取った各ファイル引数からディレクトリツリーの下に移動し、指示されているように見つかった各ファイルに対してテストを実行します。
については-type f
、本物ファイルが一般ファイルの場合間違ったそうでなければ-name <some-pattern>
、名前現在検討中のファイルのパターンと一致し、それ以外の場合はfalseです。隠しファイルや/
ハンドルやシェル参照の概念はなく、文字列(ファイル名)をパターンと一致させるだけです。
たとえば、-name '*foo[a/b]ar'
(-name
および*foo[a/b]ar
引数をに渡す)はsumとfind
一致します。決して一致しませんが、代わりにファイル名が一致するためです。foobar
.fooaar
foo/bar
-name
-path
これで引用/エスケープ形式があります。~のためfind
- ここではバックスラッシュとしてのみ認識されます。これにより、オペレータの回避が可能になります。シェルの場合、これは通常のシェル引用(\
シェルの引用メカニズムの1つ)の一部として実行されます。find
()の場合、fnmatch()
これはパターン構文の一部です。
たとえば、-name '\**'
名前が.で始まるファイルと一致します*
。または...-name '*[\^x]*'
を含むファイルと一致します。^
x
これで、find
シェルで認識されるさまざまな他の演算子に対して少なくとも共通のサブセットであることにfnmatch()
同意する必要があります。bash
*
?
[...]
特定のシェルまたはfind
実装がシステムfnmatch()
機能を使用するのか、それとも独自の機能を使用するのかは、実装によって異なります。find
少なくともGNUシステムではGNUです。シェルは仕事を複雑にし、努力する価値がないので、それを使用しません。
bash
当然じゃない。 ksh、bash、zshなどの最新のシェルには、グローブ動作に影響を与える、、、および*
多くのオプションと特殊引数(/)の拡張もあります。?
[...]
GLOBIGNORE
FIGNORE
また、fnmatch()
シェルパターンマッチングを実装することに加えて、以下があります。glob()
シェルワイルドカードと同様の機能を実装します。
これらのさまざまな実装では、パターンマッチング演算子間に微妙な違いがある可能性があります。
たとえば、GNUの場合fnmatch()
、、、またはは有効な文字を形成しないバイトまたはバイトシーケンスと一致しませんが(および?
他のほとんどのシェルでは)一致します。たとえば、GNUシステムでは、名前に無効な文字が含まれているファイルは一致しない可能性がありますが、リストされます(で始まらない限り)。*
[!x]
bash
find . -name '*'
bash -c 'echo *'
.
私たちは、引用が引き起こす可能性のある混乱についてすでに言及しました。