Shell

find(1):星號萬用字元是如何在某些文件名上失敗的?

  • April 29, 2015

在文件名為 UTF-8 的文件系統中,我有一個名稱錯誤的文件;它顯示為:D�sinstaller,根據 zsh 的實際名稱: D$'\351'sinstaller,Latin1 表示Désinstaller,它本身是“解除安裝”的法語野蠻。Zsh 不會匹配它,[[ $file =~ '^.*$' ]]但會匹配一個 globbing *——這是我所期望的行為。

現在我仍然希望在執行時找到它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_commonin pred.c) 上的行為:

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

此程式碼測試 0 的返回值fnmatch是否相等,但不檢查錯誤;這會導致任何錯誤被報告為“不匹配”。

許多年前,有人建議更改此 libc 函式的行為,使其始終在*模式上返回 true,即使在損壞的文件名上也是如此,但據我所知,這個想法一定已被拒絕(請參閱從https開始的執行緒://sourceware.org/ml/libc-hacker/2002-11/msg00071.html):

> > 當 fnmatch 檢測到無效的多字節字元時,它應該回退到單字節匹配,以便 “*” 有機會匹配這樣的字元串。 > > >

為什麼這更好或更正確?有現成的做法嗎?

正如 Stéphane Chazelas 在評論中提到的,也在同一個 2002 執行緒中,這與 shell 執行的 glob 擴展不一致,shell 不會阻塞無效字元。也許更令人費解的是,逆向測試將僅匹配那些名稱損壞的文件(在 bash 中使用 . 創建文件touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'):

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

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

因此,要回答您的問題,您可以通過了解您fnmatch在這種情況下的行為以及如何find處理此函式的返回值來預測這一點;您可能無法僅通過閱讀文件來發現。

引用自:https://unix.stackexchange.com/questions/195337