web-dev-qa-db-ja.com

find(1):一部のファイル名で失敗するスターワイルドカードはどのように実装されていますか?

ファイル名がUTF-8であるファイルシステムで、誤った名前のファイルがあります。 D�sinstaller、zshに基づく実際の名前:D$'\351'sinstallerDésinstallerのLatin1、それ自体が「アンインストール」のフランス語の蛮族Zshは[[ $file =~ '^.*$' ]]とは一致しませんが、グロビング*とは一致します。これは私が期待する動作です。

今でも、find . -name '*'を実行しているときにそれを見つけることを期待しています。実際、ファイル名がこのテストに失敗することは決してありません。ただし、LANG=en_US.utf8を使用すると、ファイルが表示されないため、LANG=C(またはen_US、または'')を設定して機能させる必要があります。

質問:背後にある実装は何ですか、そしてどのようにしてその結果を予測できましたか?

情報:Arch Linux 3.14.37-1-lts、find(GNU findutils)4.4.2

32
Michaël

それは本当にいいキャッチです。 GNU findのソースコードをざっと見てみると、これはfnmatchが無効なバイトシーケンス(pred_name_common in pred.c ] _):

b = fnmatch (str, base, flags) == 0;
(...)
return b;

このコードは、fnmatchの戻り値が0と等しいかどうかをテストしますが、エラーはチェックしません。これにより、「一致しない」と報告されるエラーが発生します。

何年も前に、このlibc関数の動作を変更して、*パターンで、壊れたファイル名であっても常にtrueを返すように提案しましたが、私が知ることができるものから、拒否されたに違いありません( https://sourceware.org/ml/libc-hacker/2002-11/msg00071.html )で始まるスレッド:

Fnmatchが無効なマルチバイト文字を検出すると、シングルバイトマッチングにフォールバックするため、 "*"はそのような文字列に一致する可能性があります。

そして、なぜこれがより良いか、より正しいのですか?既存の慣行はありますか?

StéphaneChazelasがコメントで述べたように、また同じ2002年のスレッドでも、これは無効な文字を詰まらせないシェルによって実行されるグロブ拡張と矛盾しています。おそらくさらに不可解なのは、テストを元に戻すと、名前が壊れているファイルのみに一致するという事実です(touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'を使用してbashにファイルを作成します)。

$ find -name '*'
.
./Touché
./日本語

$ find -not -name '*'
./D?marrer

したがって、質問に答えるために、この場合のfnmatchの動作を知り、findがこの関数の戻り値をどのように処理するかを知ることで、これを予測できます。おそらく、ドキュメントを読んだだけではわかりませんでした。

26
dhag

find-nameオプションはシェル パターンマッチング表記 を使用して、ファイル名のマッチングを実行します。 *はパターン 複数の文字に一致 、0個以上の文字列に一致します。

findfnmatch を使用してパターンマッチングをチェックするため、 ltrace を使用して結果を確認できます。

$ touch $'\U1212'aa
$ touch D$'\351'sinstaller
$ LC_ALL=en_US.utf8 ltrace -e fnmatch find -name '*'          
find->fnmatch("foo", "foo", 0)                   = 0
find->fnmatch("Foo", "foo", 0)                   = 1
find->fnmatch("Foo", "foo", 16)                  = 0
find->fnmatch("*", ".", 0)                       = 0
.
find->fnmatch("*", "D\351sinstaller", 0)         = -1
find->fnmatch("*", "\341\210\222aa", 0)          = 0
./ሒaa
+++ exited (status 0) +++

D\351sinstallerを使用すると、fnmatch-1を返し、一致しなかったことを示します。 ሒaaなどの有効な文字が照合されます。

あなたの場合、UTF-8ロケールでは、\351は無効な文字であり、パターンマッチングが失敗します。

13
cuonglm