查找和删除具有非ascii名称的文件

Roh*_*pra 17 linux bash shell

我有一些旧的迁移文件包含不可打印的字符.我想找到所有具有此类名称的文件,并从系统中完全删除它们.

例:

ls -l
-rwxrwxr-x 1 cws cws      0 Dec 28  2011 ??"??

ls -lb
-rwxrwxr-x 1 cws cws      0 Dec 28  2011 \a\211"\206\351
Run Code Online (Sandbox Code Playgroud)

我想找到所有这些文件.

以下是我ls在此类文件夹中执行操作时所看到的示例屏幕截图:

在此输入图像描述

我想用不可打印的字符找到这些文件,然后删除它们.

Thi*_*Not 27

非ASCII字符

ASCII字符代码范围从十六进制0x000x7F十六进制.因此,代码大于的任何字符0x7F都是非ASCII字符.这包括UTF-8中的大部分字符(ASCII代码本质上是UTF-8的子集).例如,日文字符

以十六进制编码为UTF-8

E3 81 82

UTF-8一直是默认的字符编码,其中包括自版本8.0(2002)以来的Red Hat Linux,自版本9.1(2004)以来的SuSE Linux,以及自5.04版(2005)以来的Ubuntu Linux.

ASCII控制字符

在ASCII代码中,0x00通过0x1F0x7F表示控制字符,例如ESC(0x1B).这些控制字符最初并不打算是可打印的,即使它们中的一些(如换行符0x0A)可以被解释和显示.

在我的系统上,默认ls显示所有控制字符?,除非我通过该--show-control-chars选项.我猜你要删除的文件包含ASCII控制字符,而不是非ASCII字符.这是一个重要的区别:如果删除包含非ASCII字符的文件名,则可能会破坏恰好以其他语言命名的合法文件.

字符代码的正则表达式

POSIX

POSIX提供了一个非常方便的字符类集合来处理这些类型的字符(感谢bashophil指出这一点):

[:cntrl:] Control characters
[:graph:] Graphic printable characters (same as [:print:] minus the space character)
[:print:] Printable characters (same as [:graph:] plus the space character)
Run Code Online (Sandbox Code Playgroud)

PCRE

Perl兼容的正则表达式允许使用语法的十六进制字符代码

\x00
Run Code Online (Sandbox Code Playgroud)

例如,日语字符的PCRE正则表达式?将是

\xE3\x81\x82
Run Code Online (Sandbox Code Playgroud)

除了上面列出的POSIX字符类之外,PCRE还提供了[:ascii:]字符类,这是一种方便的简写[\x00-\x7F].

GNU的版本grep支持PCRE使用-P标志,但BSD grep(例如在Mac OS X上)不支持.GNU和BSD都不find支持PCRE正则表达式.

查找文件

GNU find支持POSIX正则表达式(感谢iscfrc指出纯find解决方案以避免产生其他进程).以下命令将列出当前目录下包含不可打印控制字符的所有文件名(但不是目录名):

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$'
Run Code Online (Sandbox Code Playgroud)

正则表达式有点复杂,因为-regex选项必须匹配整个文件路径,而不仅仅是文件名,因为我假设我们不想仅仅因为它们位于包含名称的目录中而使用普通名称来吹走文件控制字符.

要删除匹配的文件,只需将-delete选项传递给find,在所有其他选项之后(这很关键; -delete作为第一个选项传递将吹走当前目录中的所有内容):

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -delete
Run Code Online (Sandbox Code Playgroud)

强烈建议您在没有-delete一个命令的情况下运行该命令,这样您就可以在太晚之前看到将要删除的内容.

如果还传递了该-print选项,则可以在命令运行时查看正在删除的内容:

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -print -delete
Run Code Online (Sandbox Code Playgroud)

要删除包含控制字符的任何路径(文件目录),可以简化正则表达式,并且可以删除该-type选项:

find -regextype posix-basic -regex '.*[[:cntrl:]].*' -print -delete
Run Code Online (Sandbox Code Playgroud)

使用此命令,如果目录名称包含控制字符,即使没有该目录内的文件名的做,他们将全部被删除.


更新:查找非ASCII 控制字符

看起来您的文件包含非ASCII字符 ASCII控制字符.事实证明,[:ascii:]不是一个POSIX字符类,但它由PCRE提供.我找不到POSIX正则表达式,所以它是Perl救援.我们仍将使用find遍历目录树,但我们会将结果传递给Perl进行处理.

为了确保我们可以处理包含换行符的文件名(在这种情况下似乎很可能),我们需要使用-print0参数find(在GNU和BSD版本上都支持); 这会将记录与空字符(0x00)而不是换行符分开,因为空字符是Linux上唯一不能存在于有效文件名中的字符.我们需要将相应的标志传递-0给我们的Perl代码,以便它知道记录是如何分开的.以下命令将以递归方式打印当前目录中的每个路径:

find . -print0 | perl -n0e 'print $_, "\n"'
Run Code Online (Sandbox Code Playgroud)

请注意,此命令仅生成Perl解释器的单个实例,这有利于提高性能.起始路径参数(在本例中.为for CWD)在GNU中是可选的,findfind在Mac OS X上的BSD中是必需的,所以我为了便携性而包含它.

现在为我们的正则表达式.这是一个PCRE正则表达式匹配名称,包含非ASCII或不可打印(即控制)字符(或两者):

[[:^ascii:][:cntrl:]]
Run Code Online (Sandbox Code Playgroud)

以下命令将打印当前目录中与此正则表达式匹配的所有路径(目录文件):

find . -print0 | perl -n0e 'chomp; print $_, "\n" if /[[:^ascii:][:cntrl:]]/'
Run Code Online (Sandbox Code Playgroud)

chomp是必要的,因为它从每个路径中剥离尾随的空字符,否则将匹配我们的正则表达式.要删除匹配的文件和目录,我们可以使用以下内容:

find . -print0 | perl -MFile::Path=remove_tree -n0e 'chomp; remove_tree($_, {verbose=>1}) if /[[:^ascii:][:cntrl:]]/'
Run Code Online (Sandbox Code Playgroud)

这也会在命令运行时打印出要删除的内容(尽管控制字符被解释为输出与输出不完全匹配ls).

  • 另外你也可以使用`[:print:]`或`[:graph:]`,参见http://www.faqs.org/docs/abs/HTML/regexp.html (2认同)

ken*_*orb 6

根据这个答案,尝试:

LC_ALL=C find . -regex '.*[^ -~].*' -print # -delete
Run Code Online (Sandbox Code Playgroud)

或者:

LC_ALL=C find . -type f -regex '*[^[:alnum:][:punct:]]*' -print # -delete
Run Code Online (Sandbox Code Playgroud)

注意:文件打印正确后,删除该#字符。

另请参阅:如何 grep 查找所有非 ASCII 字符