find(1):星號萬用字元是如何在某些文件名上失敗的?
在文件名為 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_common
inpred.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
處理此函式的返回值來預測這一點;您可能無法僅通過閱讀文件來發現。