(其中一部分是从previous answer of mine 窃取的。)
Unicode 允许一些重音字符以几种不同的方式表示:作为表示重音字符的“代码点”,或作为表示字符的非重音版本的一系列代码点,后跟重音。例如,“ñ”可以表示为预先组合为 U+00F1(UTF-8 0xc3b1,带有波浪号的拉丁小写字母 n)或分解为 U+006E U+0303(UTF-8 0x6ecc83,拉丁小写字母 n + 组合波浪号) )。
OS X 的 HFS+ 文件系统要求所有文件名都以完全分解形式的 UTF-8 表示形式存储(除了一些与此处无关的例外)。在 HFS+ 文件名中,“ñ”必须编码为 0x6ecc83。
当您在键盘上键入“ñ”时,它使用组合形式 U+00F1 (0xc3b1)。您可以通过十六进制转储看到这一点:
$ echo ñ | xxd
00000000: c3b1 0a ...
(注意:“0a”是echo 输出“行”末尾的换行符。)但是当您在 MacOS 扩展卷上的文件名中使用它时,它会转换为分解形式U+006E U+0303 (0x6ecc83):
$ 触摸ñ
$ ls | xxd
00000000: 6ecc 830a n...
在 UTF-8 语言环境中,这两种不同的表示应该被视为同一个字符,但显然 macOS 中的 find 不这样做:
$ LC_ALL=en_US.UTF-8 find . -name '*ñ*'
$ LC_ALL=en_US.UTF-8 find . -name '*n*'
./ñ
$ LC_ALL=en_US.UTF-8 find . -name 'n?'
./ñ
在第二个和第三个命令中,find 与“n”代码点匹配,并将组合波浪号视为跟在它后面的一个完全独立的字符。顺便说一句,请注意,我在匹配模式周围加上了引号——这很重要,因为没有它们,shell 会将其扩展为当前目录中的文件名列表,然后将其传递给find 命令。
解决方案?好吧,有一个令人讨厌的选项,即在模式中显式使用分解的形式。您可以使用 bash 的 $' ... ' 引用形式来做到这一点,它允许使用 \x 指定十六进制字节:
$ find . -name $'*n\xcc\x83*'
./ñ
但实际上比这更糟糕,因为从 macOS High Sierra 开始,Apple 使用新的 Apple 文件系统 (APFS),它允许 both 表示。而且由于find 无法将它们识别为字符,因此您甚至不能使用像-name *[ññ]*' to match both of them, you have to use an extended regular expression with-Eand-regex` 这样的括号表达式,就像这样(在带有APFS 的Mac 上完成):
$ touch composed-ñ decomposed-n$'\xcc\x83' unaccented-n
$ ls
composed-ñ decomposed-ñ unaccented-n
$ ls | xxd
00000000: 636f 6d70 6f73 6564 2dc3 b10a 6465 636f composed-...deco
00000010: 6d70 6f73 6564 2d6e cc83 0a75 6e61 6363 mposed-n...unacc
00000020: 656e 7465 642d 6e0a ented-n.
$ find -E . -regex $'.*(\xc3\xb1|n\xcc\x83).*'
./composed-ñ
./decomposed-ñ
(请注意,在正则表达式中,.* 是匹配任何字符序列的方式,相当于普通“glob”通配符模式中的*。)
自己动手支持 Unicode 是不是很有趣?