ファイル名がUTF-8であるファイルシステムで、誤った名前のファイルがあります。 D�sinstaller
、zshに基づく実際の名前:D$'\351'sinstaller
、Dé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
それは本当にいいキャッチです。 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
がこの関数の戻り値をどのように処理するかを知ることで、これを予測できます。おそらく、ドキュメントを読んだだけではわかりませんでした。
find-name
オプションはシェル パターンマッチング表記 を使用して、ファイル名のマッチングを実行します。 *
はパターン 複数の文字に一致 、0個以上の文字列に一致します。
find
は fnmatch を使用してパターンマッチングをチェックするため、 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
は無効な文字であり、パターンマッチングが失敗します。