在Linux中删除所有没有以下扩展名的文件。

19

我有一个扩展名列表:

avi,mkv,wmv,mp4,mp5,flv,M4V,mpeg,mov,m1v,m2v,3gp,avchd

我想在Linux中删除指定扩展名以外的所有文件,以及没有扩展名的文件。

如何使用rm命令实现这个功能?

3个回答

33

首先,您需要找到不包含这些扩展名的文件。您可以使用 find 命令非常容易地完成此操作。您可以基于以下命令构建 -

find /path/to/files ! -name "*.avi" -type f -exec rm -i {} \;
你也可以使用-regex代替-name来进行复杂的搜索模式匹配。使用!来否定搜索,这样就能够列出不包含这些扩展名的文件。
在使用rm -i删除文件之前最好先列出所有文件,这样做可以避免误删,但如果列表很长的话可能会比较烦琐,你可以自行决定是否使用该命令。
使用这个方法删除大量文件可能是有风险的,一旦删除就无法恢复。因此,在执行rm命令之前应该先用find命令检查列表,确保没有误删的文件。
更新:
正如aculich在评论中所述,你也可以采用以下方法 -
find /path/to/files ! -name "*.avi" -type f -delete

-type f将确保只查找删除普通文件,不会触及任何目录、符号链接等


6
我建议使用-delete而不是-exec rm; 您的实现可能已经有了,所以请使用它! - aculich
4
在这个特定的例子中,可能不太可能只列出具有这些扩展名的文件而非目录,但您可以添加“-type f”参数来实现(注:这是一句翻译好的句子,原文已经在上面了)。 - aculich

13
你可以使用一个快速而简单的rm命令来完成想要的任务,但请记住它是容易出错、不可移植、危险且有严重限制
正如其他人建议的那样,你可以使用find命令。我几乎在所有情况下都建议使用find而不是rm
由于你提到你在Linux系统上,我将在我的示例中使用GNU实现,它是findutils包的一部分,因为它是大多数Linux系统上的默认实现,并且通常建议学习它,因为它具有比许多其他实现更丰富和更先进的功能。
尽管它可能令人生畏并且看起来过于复杂,但值得花时间掌握find命令,因为它给你一种精确的表达能力和安全性,你在大多数其他方法中都无法找到这种能力,除非你本质上(糟糕地)重新发明了这个命令! < h2 >查找示例 人们经常建议以find命令的方式进行操作,但这种方式效率低下、容易出错且危险。因此,在下面我将概述一种安全有效的方法,来完成你在示例中所要求的内容。

在删除文件之前,我建议先预览文件列表(如果列表非常长,则至少预览部分文件):

find path/to/files -type f -regextype posix-extended -iregex '.*\.(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)$'

上述命令将显示您要删除的文件列表。要实际删除文件,只需添加-delete操作即可:
find path/to/files -type f -regextype posix-extended -iregex '.*\.(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)$' -delete

如果您想查看将保留的内容,可以通过在预览命令中添加!不包括-delete)来反转匹配项,例如:
find path/to/files -type f -regextype posix-extended ! -iregex '.*\.(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)$'

这个反向匹配的输出结果应该与在执行删除后列出文件时看到的输出结果相同,除非由于权限问题或不可写的文件系统而发生错误:
find path/to/files -type f

解释

在这里,我将深入解释我选择的选项以及原因:

我添加了-type f仅限于匹配文件;如果没有它,它将匹配非文件,例如目录,这可能不是您想要的。还要注意,我将其放在开头而不是结尾,因为谓词的顺序可能会影响速度;使用-type f首先它将针对仅文件执行正则表达式检查,而不是针对所有内容...在实践中,除非您有大量的目录或非文件,否则可能并不重要。但是,在某些情况下,保持谓词的顺序值得考虑,因为它可能会产生重大影响。

我使用了不区分大小写-iregex选项,而不是区分大小写的-regex选项,因为我认为您想使用不区分大小写的匹配,因此它将包括.wmv.WMV文件。

您可能希望使用扩展POSIX正则表达式以实现简单和简洁。不幸的是,目前还没有-regextype posix-extended 的简写,但即使如此,我仍然建议使用它,因为您可以避免在更长、更复杂的正则表达式中添加大量的 \ 反斜杠转义问题,并且它具有更高级(现代)的功能。GNU实现默认使用emacs风格的正则表达式,如果您不习惯它们,可能会感到困惑。 -delete选项应该很容易理解,但有时人们建议使用更慢、更复杂的-exec rm {} \;选项,但通常是因为他们不知道更安全、更快、更容易的-delete选项(在极少数情况下,您可能会遇到一个古老版本的find没有这个选项)。了解-exec的存在很有用,但在删除文件时尽量使用-delete。此外,不要将find的输出通过管道符|传递给另一个程序,除非您使用并理解了-print0选项,否则当您遇到带有空格的文件时,您将会遇到麻烦。
我明确包含了path/to/files参数。如果您将其省略,它将隐式地使用.作为路径,但明确指定路径更安全(特别是使用-delete)。

替代的find实现

即使您说您使用的是Linux系统,我也会提到您将遇到的BSD实现的差异,其中包括Mac OS X!对于其他系统(例如旧的Solaris盒子),祝你好运!升级到更现代的find变体!
此示例中的主要区别在于正则表达式。 BSD变体默认使用基本POSIX正则表达式。为了避免在regexes中需要繁琐的额外转义以满足basic-PRE,您可以利用扩展-PRE的更现代功能,通过指定BSD变体的-E选项来实现与使用-regextype posix-extended的GNU变体相同的行为。
find -E path/to/files -iregex '.*\.(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)$' -type f

请注意,在这种情况下,-E选项在path/to/files之前出现,而对于GNU的-regextype posix-extended选项则在路径之后出现。
很遗憾,GNU目前还没有提供-E选项(还没有!);由于我认为这将是一个有用的选项,以便与BSD变体保持一致,我将提交一个补丁到findutils中添加此选项,如果被接受,我将相应更新这个答案。

rm - 不推荐使用

虽然我强烈建议不要使用rm,但我将给出如何完成您特定问题的示例(带有一些警告)。
假设您使用的是具有Bourne语法的shell(通常是Linux系统默认的Bash shell),您可以使用以下命令:
for ext in avi mkv wmv mp4 mp5 flv M4V mpeg mov m1v m2v 3gp avchd; do rm -f path/to/files/*.$ext; done

如果您使用Bash并打开扩展的globbing功能(使用shopt -s extglob),那么您可以使用文件名扩展的模式匹配
rm -f path/to/files/*.+(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)

+(pattern-list) 扩展的 globbing 语法将匹配给定模式的一个或多个出现。

然而,我强烈建议不要使用 rm,因为:

它容易出错且危险,因为很容易在 * 之间意外添加空格,这意味着您将删除 所有;您无法预览命令的结果;它是“点火并忘记”的,所以祝好运。

它是非便携的,因为即使它在您特定的 shell 中工作,同一条命令可能在其他 shell(包括其他 Bourne-shell 变种,如果您倾向于使用 Bash-ism)中无法工作。

它有严重的限制,因为如果您有嵌套在子目录中的文件,甚至只是单个目录中有大量文件,那么在使用文件 globbing 时,您很快就会遇到命令行长度的限制。

我希望 rm 命令会彻底消失,因为除了(即使是古老的实现)find,我几乎找不到比它更好用的地方了。


4

使用Bash,您可以先启用extglob选项:

$ shopt -s extglob

然后执行以下操作:

$ rm -i !(*.avi | *.mkv | *.wmv | *.mp4)

(注:此命令将删除除.avi、.mkv、.wmv和.mp4以外的所有文件,并在删除前提示确认)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接