如何删除文件名中包含非打印字符的文件


46

我以某种方式设法创建了一个似乎没有文件名的文件。我在以下线程中找到了一些有关如何获取文件更多详细信息的信息

但是,我尝试了列出的一些建议,但似乎无法删除该文件。我不确定该如何创建,但是在尝试复制xml文件时发生了。

文件上的一些信息如下:

> ls -lb
total 296
-rw-r--r--   1 voyager  endeavor  137627 Jan 12 12:49 \177

> file *
:               XML document

> ls -i
 417777   

我试图找到使用inum开关,然后将其通过管道传递到rm,因为这似乎是摆脱它的最简单的方法。但是,下面链接下面的线程底部给出的示例对我来说失败了。示例是:

> find -inum 41777 -exec ls -al {} \;
find: illegal option -- i
find: [-H | -L] path-list predicate-list

因此,我首先尝试使用路径列表,如下所示,但这也不起作用:

> find . -inum 41777 -exec ls -al {} \;

我不确定\ 177是什么不可打印字符或如何将其传递给rm命令,但是我真的想确保在尝试删除此文件时不会弄乱其他任何文件/目录。


1
\177是ASCII DEL字符。
基思·汤普森

9
417777!= 41777
CVn

迈克尔(Michael)表现出色。这可能就是为什么我的命令从未起作用的原因。
穆斯先生2012年

@KeithThompson在没有前导的情况下是否为八进制0

1
@cat:在这种情况下,是的。对于字符串和字符文字,它遵循C语法。
基思·汤普森

Answers:


46

该文件有一个名称,但是由不可打印的字符组成。如果您使用ksh93,bash,zsh,mksh或FreeBSD sh,则可以尝试通过指定其不可打印的名称来将其删除。首先使用以下命令确保名称正确:ls -ld $'\177' 如果显示正确的文件,则使用rm:rm $'\177'

另一种(风险更高)的方法是使用rm -i -- *。使用-i选项,rm要求您在删除文件之前进行确认,因此,您可以跳过要保留的所有文件,仅跳过其中的一个。

祝好运!


1
我怀疑rm -i *在这种情况下是行不通的。由于存在换行符,shell会扩展*rm无法识别为文件名的名称。
基思·汤普森

6
@KeithThompson:外壳中的Glob扩展无需进一步解释。除非扩展名中包含换行符,否则不会有换行符。
ChristofferHammarström,2012年

@ChristofferHammarström:嗯。我将做一些实验并做进一步评论。
基思·汤普森

@ChristofferHammarström:扩展名中包含换行符,因为文件名包含换行符。但是似乎我bash和GNU 都低估了rm。在我的系统,rm *rm -i *,和rm Ctrl-V DEL TAB ENTER一切工作正常,即使文件名包含一个换行符(我用的"\177foo\nbar\n")。有些命令不能很好地处理此类文件名,但是GNU工具似乎很健壮。相同工具的非GNU版本可能无法正常运行。无论如何,这些答案中的各种技巧都是有用的。
基思·汤普森

1
@KeithThompson,任何外壳程序都可以正确处理文件冲突,不仅是Bash,而且rm无论是否为GNU ,都不会进行任何形式的单词拆分rm。Shell扩展了全局对象,并在执行所需的命令时将生成的文件名作为空分隔的参数(在C代码中)传递。危险的是将文件列表通过管道传递到使用换行符作为分隔符的文件。请参阅为什么遍历find的输出是错误的做法?
通配符

23

对于那些使用vim它的人,请在当前工作目录中运行它:

$ vim ./ 

并使用箭头键或导航到文件j/k。然后点击Shift+D并确认删除y


我通常会害怕vim,但是效果确实很好
tofutim

9

可能存在一种将文件名传递给的方法rm,但是如果您担心将任何内容弄乱,则可以使用GUI文件管理器。Emacs带有目录编辑模式,如果已安装,可以使用它:

  1. 在中打开文件夹emacs。您可以运行emacs /path/to/folder,或打开emacs,点击Esc+ x并运行dired,这将提示您输入路径

    http://so.mrozekma.com/unix-dired-delete1.png

  2. 移至要删除的文件所在的行,然后按d。您应该D在文件旁边的左边距中看到一个:

    http://so.mrozekma.com/unix-dired-delete2.png

  3. x保存更改。它将提示您确定要删除该文件。按y

    http://so.mrozekma.com/unix-dired-delete3.png


1
看起来是个好主意,但我似乎在这台机器上找不到emacs。冒着发动圣战的危险...这可以在vim中完成吗?
穆斯先生2012年

2
对于那些使用vim它的用户,请在当前工作目录中运行它:vim ./并使用箭头键导航到该文件。然后点击Shift+D并确认删除y

@黑森哇。我没想到vim会支持
Michael Mrozek

1
@MichaelMrozek同意,它具有我偶尔会发现的所有这些功能。

1
Tim

8

您已经演示file *了shell glob(*)能够提取该文件,因此现在只需rm对该glob进行操作即可。

  • 转到文件所在的目录。
  • rm -i *
  • 输入n所有文件,但文件名看似不可见的文件除外

7

该文件实际上有一个名称,但是它由不可打印的字符组成。

一种简单的方法是依靠外壳补全:Tab反复按直到插入“奇怪”的文件名。您必须将Shell配置为在可能的完成之间循环:

  • 在bash中,您需要调用menu-complete,默认情况下未绑定,而不是默认情况下complete已绑定Tab的。或者,用于Esc *为所有文件插入补全,并从命令行中删除您不想删除的补全。
  • 在zsh中,默认设置很好。您需要拥有setopt auto_menusetopt menu_complete设置。

如果完成对您的外壳没有帮助,则可以调用rm -i -- *并回答“否”,但此特定文件除外。如果文件名不是以可打印字符开头,则可以将匹配项限制为第一个字符不在一组可打印字符中的文件:

rm -i [!!-~]*
rm -i [![:print:]]*
rm -i -- [!a-z]

(请注意,您的模式是否可以匹配名为的文件-f,该文件rm将作为选项)。

另一种方法是调用ls -i以查找文件的索引节点(索引节点唯一地标识给定文件系统上的文件),然后find -inum对该特定文件进行操作。当目录包含许多文件时,此方法最有用。

ls -i
# note the inode number 12345
find . -xdev -inum 12345 -delete

如果您find没有-delete选择,请致电rm

find . -xdev -inum 12345 -exec rm -- {} \;

(是的,我知道其中大多数已经发布。但是有关信息的答案使这一过程变得比原本要复杂的多。)


4

尝试使用echo -e获取角色\177,然后xargs将其传递给rmecho不使用十进制转义符,因此转换为十六进制:结果177是八进制;echo在以下条件之前需要一个\0(文字,非空字节):

echo -ne '\0177\0' | xargs -0 rm

您是说\ xb1 \ 0是\ 177的十六进制表示吗?
穆斯先生2012年

@MrMoose \xb1是十进制数177,以\0null终止,并-0告知xargs采用以null终止的名称,而不是以换行符终止的名称。
凯文(Kevin)

我在SunOS上使用bash,当我尝试输入命令时,得到了xargs:非法选项-0。我看了手册页,看不到空终止选项。我现在设法解决了这个问题。请参阅标记为答案的回复。
穆斯先生2012年

echo,作为-是,将2文件名xargs的,然后就到rm......第二个文件名\n。这导致无论是一个错误,或删除的域名(“\ n”)的文件
彼得。 O

@perer是的,我只是注意到了。我添加-n以摆脱换行符。
凯文(Kevin)

3

我可以保证您该文件确实具有名称。它恰好是包含不可打印字符的名称。

如果rm不起作用,则将findwith与-inum选项的输出用作to的参数rm不太可能有帮助;它只是将相同的参数传递给rm,具有相同的问题。

该文件是该目录中的唯一文件,对吗?

我要尝试的第一件事是cd进入目录,然后输入rm ./,然后按Tab键。这应该将参数扩展为文件的实际名称,并且前面的名称./应避免rm将参数解释为文件名以外的其他名称。

另一种解决方案是转到cd父目录,然后在目录上使用rm -r(或,如有必要rm -rf)。那应该删除目录及其所有内容,无论名称如何。然后,您可以mkdir用来重新创建目录(也许还chmod可以恢复其权限)。

编辑:

ls -lb再次查看您的输出,我认为文件名以ASCII DEL字符开头(很糟糕,不是致命的),并且包含一个或多个换行符(真的很糟糕)。据我所知,该rm命令无法将换行符解释为文件名的一部分。

但是unlink()系统调用可以。这是我放在一起的一个Perl脚本,它将遍历当前目录中的文件,并询问您是否要删除它。它不使用诸如rm删除文件之类的外部命令,因此它应该能够处理文件系统支持的所有有趣字符(ASCII NUL和以外的任何字符/)。

在包含目录上执行rm -rrm -rf操作仍然应该起作用,但是如果您只想删除单个文件,则可以尝试此操作。

#!/usr/bin/perl

use strict;
use warnings;

opendir my $DIR, '.' or die ".: $!\n";
my @files = sort grep { -f } readdir $DIR;
closedir $DIR;

foreach my $file (@files) {
    my $pfile = printable($file);
    print "Remove $pfile? ";
    my $answer = scalar <STDIN>;
    if ($answer =~ /^[Yy]/) {
        print "Removing $pfile\n";
        unlink $file or warn "Unable to remove $pfile: $!\n";
    }
}

sub printable {
    my($s) = @_;
    $s =~ s/[^ -~]/?/g;
    return $s;
}

(如果您要保留目录中的其他文件,另一种解决方案是将所有其他文件移动到一个临时目录中,然后核对并重新创建目录,然后将其他文件移回。)

(哦,无论您做什么来创建该文件,都不要再做一次!)


1

如果你能找到确切使用该文件find的命令,那么你可以使用-delete谓语。请小心,并-delete在find命令中输入最后一个参数,否则会造成很多伤害。


1

你近了 分步介绍如何按inode编号删除文件。

首先,使用查找所需文件的索引节点号ls -li。索引节点号将在输出的第一列中。

例如:

$ls -li
311010 -rw-rw-r-- 1 me me 3995 Apr  6 16:27 -???\# ;-)

在这种情况下,索引节点号是311010

使用find寻找由inode编号的文件。

find . -inum 311010 

确保find仅返回要删除的文件的名称。例如:

$find . -inum 311010
./-???\# ;-)

确定find可以找到正确的文件后,请将-delete参数传递给find。它将为您删除文件。

find . -inum 311010 -delete

0

其他答案很好,几乎可以在所有环境中使用。

但是,如果您恰好正在运行gui,只需在Nautilus,Dolphin,konqueror或您最喜欢的文件管理器中打开目录,您只需单击几下鼠标,便可以轻松查看和删除名称不一致的任何文件。 。

如果您不是普通的emacs或vim用户(如其他几个答案所述),这可能是最简单的方法。


0

名为“ \ 033”的文件(实际的“转义”字符)也有类似的问题。

for x in $( ls | awk '!/^[A-Z]/ && !/^[a-z]/ && !/^[0-9]/');do rm -i -- $x ; done

上面的内容将提示您删除当前目录中不以字母或数字开头的文件。


0

相反,您可以将当前目录重命名为DirX_OLD,使用名称创建一个新目录,DirX然后将所有必需的文件从DirX_OLD移至DirX。复制文件后,您可以删除DirX_OLD


0

以防万一,它可以帮助任何人,我会加我的两分钱。我能够stat在通配符上使用这样的文件名:

stat -c %N *

输出:

`\220g\201\acontexts'
`\220g\201\alogins'
`\220g\201\amodules'
`\220g\201\apolicy'
`\220g\201\asetrans.conf'

然后,我可以使用ANSI C引号的字符串(在接受的答案中使用)来访问文件:

rm $'\220g\201\asetrans.conf'

0

每个文件都有一个名称,您只需要查找确切的名称即可。

我将使用以下文件作为示例:

$ touch $'\177' $'\001\002' $'\033\027' a b abc $'a\177b'

这些文件将?正常显示ls

$ ls
??  ?  ?002  a  abc  b

但是,如果您ls允许该-Q选项,则应清楚实际文件名是什么:

$ ls -1iQ *
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739118 "a"
26739121 "a\177b"
26739120 "abc"
26739119 "b"

这些是1pwd中文件列中的索引节点编号和“引用”名称。

那就是利用(POSIX)shell的功能来使用扩展(globbing):

要使名称具有任何非印刷字符:

$ ls -1iQ *[![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"
26739121 "a\177b"

并列出可能只包含非打印字符的文件:

$ ls -1iQ [![:print:]]*
26739122 "\001\002"
26739117 "\033\027"
26739115 "\177"

然后,要有选择地(-i(交互式)选项)将其删除,请使用:

$ rm -i [![:print:]]*
rm: remove regular file ''$'\001\002'? n
rm: remove regular file ''$'\033\027'? n
rm: remove regular file ''$'\177'? n

您只需通过回答y上述问题来选择要删除的内容即可。


0

如果您有自动补全功能,这可能对您有用。我键入了目录列表中显示的字符。然后,我触发了自动完成功能,并将其重命名为可打印的内容。

这对我有用:

$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 ?
$ mv ?  
(hit the [tab] and it gets extended to ...)
$ mv ^[
(now complete the command and rename to something printable)
$ mv ^[ weirdchar
$ ll
-rw-r--r--.  1 xxxxxxxx  xxxxx 25608 Oct 11 11:11 weirdchar
(now delete)
$ rm weirdchar

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.