使用 find 或 grep 从不同的编码系统(Windows 到 Linux)定位带有重音字符的文件名

aso*_*ove 4 linux bash grep find

我试图在stackoverflow上标记一个类似于我的问题(在Linux文件系统上查找非UTF8文件名)以引出进一步的答复,到目前为止没有运气,所以这里再次......

我和上面链接中的 OP 有同样的问题,convmv 是修复自己文件系统的好工具。因此,我的问题是学术性的,但我发现“查找”无法找到非标准的 ascii 字符这一点令人不满意(实际上我无法相信)。

有没有人知道使用什么选项组合来查找包含非标准字符的文件名的文件名似乎是 unicode FS,在我的情况下,字符似乎是 8 位扩展的 ascii 而不是 unicode,文件来自一台 Windows 机器(iso-8859-1),我经常需要获取它们。我很想看看 find 和/或 grep 如何与 convvv 做同样的事情。

示例文件:

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

> ls -b
Abc\251def  ÉÈéèáà-rest  everest  éverest
Run Code Online (Sandbox Code Playgroud)

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

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

> ls | egrep '[^a-zA-Z]'
ÉÈéèáà-rest
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

> find . -regex '.*[é].*'
./éverest
./ÉÈéèáà-rest
Run Code Online (Sandbox Code Playgroud)

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

> ls | fgrep e
Abc?def
ÉÈéèáà-rest
everest
éverest
Run Code Online (Sandbox Code Playgroud)

正如我所料,寻找一个没有争议的角色会显示所有带有 grep 的文件。

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

> find . -name '*e*'
./éverest
./ÉÈéèáà-rest
./everest
Run Code Online (Sandbox Code Playgroud)

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

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

任何见解将不胜感激。

小智 9

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

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

LANG=C find . -regex '.*[^a-zA-Z./].*'
Run Code Online (Sandbox Code Playgroud)

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

  • 运行 `LC_CTYPE=C find ...` 或 `LC_ALL=C find ...` 会更可靠。环境变量的优先级是`LC_ALL`,如果没有`LC_xxx` 用于所需的语言环境类别(这里是ctype,即字符集和编码),如果也没有`LANG`。因此,如果您已经在环境中使用了 `LC_CTYPE`(在世界上 95% 的语言不能用 ASCII 编写的情况下很常见),`LANG=` 不会改变任何事情。 (3认同)

aso*_*ove 5

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
Run Code Online (Sandbox Code Playgroud)