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

时间:2013-10-02 20:27:53

标签: 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

我想找到所有这些文件。

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

enter image description here

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

5 个答案:

答案 0 :(得分:25)

非ASCII字符

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

  

以十六进制编码为UTF-8

  

E3 81 82

UTF-8一直是Red Hat Linux since version 8.0 (2002), SuSE Linux since version 9.1 (2004), and Ubuntu Linux since version 5.04 (2005)的默认字符编码。

ASCII控制字符

在ASCII代码中,0x000x1F0x7F代表控制字符,例如ESC0x1B)。这些控制字符最初并不打算打印,即使它们中的一些(如换行符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)

PCRE

Perl兼容的正则表达式允许使用语法

的十六进制字符代码
\x00

例如,日语字符的PCRE正则表达式为

\xE3\x81\x82

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

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

查找文件

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

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$'

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

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

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -delete

高度建议首先运行命令而不 -delete,这样您就可以看到将要删除的内容为时已晚。< / p>

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

find -type f -regextype posix-basic -regex '^.*/[^/]*[[:cntrl:]][^/]*$' -print -delete

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

find -regextype posix-basic -regex '.*[[:cntrl:]].*' -print -delete

使用此命令,如果目录名称包含控制字符,即使目录中没有任何文件名,也会删除 all


更新:查找非ASCII 控制字符

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

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

find . -print0 | perl -n0e 'print $_, "\n"'

请注意,此命令仅生成Perl解释器的单个实例,这有助于提高性能。起始路径参数(在本例中为.的{​​{1}})在GNU CWD中是可选的,但在Mac OS X上的BSD find中是必需的,所以我&#39;包括它是为了便于携带。

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

find

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

[[:^ascii:][:cntrl:]]

find . -print0 | perl -n0e 'chomp; print $_, "\n" if /[[:^ascii:][:cntrl:]]/' 是必要的,因为它会从每个路径中删除尾随空字符,否则它将匹配我们的正则表达式。要删除匹配的文件和目录,我们可以使用以下内容:

chomp

这也将打印输出命令时删除的内容(尽管解释了控制字符,因此输出与find . -print0 | perl -MFile::Path=remove_tree -n0e 'chomp; remove_tree($_, {verbose=>1}) if /[[:^ascii:][:cntrl:]]/' 的输出不完全匹配)。

答案 1 :(得分:1)

到目前为止,您可能已经解决了我的问题,但它对我的案例并不适用,因为当我使用find时,-regex没有显示这些文件开关。所以我使用ls开发了此变通方法。希望它对某人有用。

基本上,对我有用的是:

ls -1 -R -i | grep -a "[^A-Za-z0-9_.':@ /-]" | while read f; do inode=$(echo "$f" | cut -d ' ' -f 1); find -inum "$inode" -delete; done

分解部分:

ls -1 -R -i

这将在当前目录下递归(-R)列表(ls)文件,每行一个文件(-1),为每个文件添加其inode编号前缀{{1} })。结果将通过管道传输到-i

grep

过滤每个条目,将每个输入视为文本(grep -a "[^A-Za-z0-9_.':@ /-]" ),即使它最终是二进制的。如果一行包含与列表中指定的字符不同的字符,-a将允许该行传递。结果将通过管道传输到grep

while

while read f do inode=$(echo "$f" | cut -d ' ' -f 1) find -inum "$inode" -delete done 将遍历所有条目,提取inode编号并将inode传递给while,然后将删除该文件。

答案 2 :(得分:0)

您只能使用grep:

打印包含反斜杠的行
ls -lb | grep \\\\

答案 3 :(得分:0)

可以将PCRE与grep -P一起使用,而不是使用find(不幸的是)。您可以使用exec链接查找grep。使用PCRE(perl regex),我们可以使用ascii类并找到任何非ascii的char。

find . -type f -exec sh -c "echo \"{}\" | grep -qP '[^[:ascii:]]'" \; -exec rm {} \;

除非第一个exec返回非错误代码,否则不会执行以下exec。在这种情况下,它表示表达式与文件名匹配。我使用sh -c因为-exec不喜欢管道。

答案 4 :(得分:0)

根据此answer,尝试:

LC_ALL=C find . -regex '.*[^ -~].*' -print # -delete

或:

LC_ALL=C find . -type f -regex '*[^[:alnum:][:punct:]]*' -print # -delete

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

另请参阅:How do I grep for all non-ASCII characters