使用 find 或 grep 查找来自不同编码系统(Windows 到 Linux)的带重音字符的文件名

使用 find 或 grep 查找来自不同编码系统(Windows 到 Linux)的带重音字符的文件名

我试图在 stackoverflow 上标记一个与我类似的问题(在 Linux 文件系统上查找非 UTF8 文件名) 以引出进一步的回复,但目前还没有成功,所以再试一次......

我和上面链接中的 OP 有同样的问题,而 convmv 是修复自己的文件系统的绝佳工具。因此,我的问题很学术,但我觉得“find”无法找到非标准 ascii 字符,这让我很不满意(事实上我不敢相信)。

有谁知道在看似 unicode FS 上查找包含非标准字符的文件名时要使用哪些选项组合,在我的例子中,字符似乎是 8 位扩展 ascii 而不是 unicode,文件来自 Windows 计算机 (iso-8859-1),我经常需要获取它们。我很想看看 find 和/或 grep 如何能做与 convmv 相同的事情。

示例文件:

> ls
Abc�def ÉÈéèáà-rest everest éverest

> ls -b
Abc\251def  ÉÈéèáà-rest  everest  éverest

第一个文件来自 Windows(或用 模拟touch $(printf "Abc\xA9def"))。

> find . -regex '.*[^a-zA-Z./].*'
./ÉÈéèáà-rest

> ls | egrep '[^a-zA-Z]'
ÉÈéèáà-rest

几乎全部都丢失了(连字符保存了该文件,可以使用彩色 grep 查看)。这里发生的一切都不是我所期望的:find 和 grep 都无法将带重音的字母视为超出提供的范围 [^a-zA-Z./]。

> find . -regex '.*é.*'
./éverest
./ÉÈéèáà-rest

> ls | egrep 'é'
ÉÈéèáà-rest
éverest

> ls | egrep '[é]'
ÉÈéèáà-rest
éverest

> find . -regex '.*[é].*'
./éverest
./ÉÈéèáà-rest

奇怪的是,两者都能够拾取标准重音(包括在范围内)。任何使用 \xA9、\0251 或 \o251 的 find 或 grep 尝试都会失败(不匹配)。

> ls | fgrep e
Abc�def
ÉÈéèáà-rest
everest
éverest

正如我所料,寻找一个没有争议的字符会用 grep 显示所有文件。

> find . -regex '.*e.*'
./éverest
./ÉÈéèáà-rest
./everest

> find . -name '*e*'
./éverest
./ÉÈéèáà-rest
./everest

但是,find 非常具有歧视性:即使查找普通字符,在我看来,它也会消除包含文件系统名称编码模式可接受字符范围之外的字符的文件名。

就我而言,如果文件在文件系统中,find 应该可以找到它,对吗?但也许有一个我不知道的功能?

任何见解都将不胜感激。

答案1

GNU 工具似乎有代码,当匹配正则表达式字符类时,如果字符编码支持,重音字母将被视为其基本字母。这旨在作为一种“按我的意思做”的功能,使编写正则表达式更容易,但在这种情况下,它会妨碍你。

尝试对“find”命令行进行以下修改:

LANG=C find . -regex '.*[^a-zA-Z./].*'

这仅在“find”命令的上下文中设置 LANG 环境变量。由于“C”语言编码仅支持 ASCII,因此重音字母将不再被视为其基本字母,因此将由您的正则表达式正确匹配。

答案2

Jander 的回答非常完美,对于那些有兴趣了解更多信息的人来说,这里还有一个提示。

使用 LANG=C 时,find 会显示带有问号的非 ASCII 字符。要将其恢复为文件系统的正常显示,只需将输出通过管道传输到 cat。

LANG=C find . -regex '.*[^a-zA-Z./-].*'
./??verest
./????????????-rest
./Abc?def

LANG=C find . -regex '.*[^a-zA-Z./-].*' | cat
./éverest
./ÉÈéèáà-rest
./Abc�def

答案3

find . | grep -E '.*[^[:print:]].*'

有关所有 posix 字符类的列表,请参阅: http://www.regular-expressions.info/posixbrackets.html

相关内容