【发布时间】:2012-01-28 08:34:44
【问题描述】:
我有一个扩展列表:
avi,mkv,wmv,mp4,mp5,flv,M4V,mpeg,mov,m1v,m2v,3gp,avchd
我想删除 linux 目录中所有没有以下扩展名的文件以及没有扩展名的文件。
如何使用 rm linux 命令执行此操作?
【问题讨论】:
我有一个扩展列表:
avi,mkv,wmv,mp4,mp5,flv,M4V,mpeg,mov,m1v,m2v,3gp,avchd
我想删除 linux 目录中所有没有以下扩展名的文件以及没有扩展名的文件。
如何使用 rm linux 命令执行此操作?
【问题讨论】:
使用 Bash,您可以先启用 extglob 选项:
$ shopt -s extglob
然后执行以下操作:
$ rm -i !(*.avi | *.mkv | *.wmv | *.mp4)
【讨论】:
您首先必须找出不包含这些扩展名的文件。您可以使用find 命令轻松完成此操作。您可以在以下命令上构建 -
find /path/to/files ! -name "*.avi" -type f -exec rm -i {} \;
您还可以使用-regex 代替-name 来输入复杂的搜索模式。 ! 是否定搜索。因此它将有效地列出那些不包含这些扩展名的文件。
最好使用rm -i,因为它会在删除之前列出所有文件。如果您的列表很全面,可能会变得乏味,因此您可以自行决定是否包含它。
使用它删除大量文件可能危险。一旦删除,您将永远无法找回它们。因此,请确保在没有 rm 的情况下运行 find 命令,以在删除它们之前彻底检查列表。
更新:
正如aculich 在 cmets 中所述,您还可以执行以下操作 -
find /path/to/files ! -name "*.avi" -type f -delete
-type f 将确保它只会处理find 和delete 常规文件,并且不会触及任何目录、符号链接等。
【讨论】:
-delete 而不是-exec rm;你的实现可能有它,所以使用它!
-type f 以仅列出具有这些扩展名的文件而不是目录(尽管在此特定示例中这可能不太可能)。
您可以使用快速而肮脏的rm 命令来完成您想要的操作,但请记住它容易出错、不可移植、危险并且有严重的限制。。 p>
正如其他人所建议的,您可以使用find 命令。我建议在几乎所有情况下都使用find 而不是rm。
既然您提到您使用的是 Linux 系统,我将在我的示例中使用 GNU implementation,它是 findutils package 的一部分,因为它是大多数 Linux 系统的默认设置,并且我通常建议您学习它,因为它有比许多其他实现更丰富、更高级的功能集。
虽然它可能令人生畏并且看起来过于复杂但值得花时间掌握find 命令,因为它为您提供了一种精确的表现力和安全性如果不从本质上(很差地)重新发明此命令,您将无法使用大多数其他方法找到它!
人们经常建议以低效、容易出错和危险的方式使用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 选项,因为我假设您想使用不区分大小写的匹配,因此它将同时包含 @987654344 @ 和 .WMV 文件。
为了简单起见,您可能希望使用extend POSIX regular expressions。不幸的是,还没有 -regextype posix-extended 的简写,但即使如此,我仍然建议使用它,因为您可以避免必须添加大量 \ 反斜杠以在更长、更复杂的正则表达式中转义的问题,并且它具有更高级(现代)的功能。 GNU 实现默认为emacs-style regexes,如果您不习惯它们,可能会感到困惑。
-delete 选项应该很有意义,但有时人们建议使用更慢、更复杂的-exec rm {} \; 选项,但通常这是因为他们不知道更安全、更快、更简单的-delete 选项(在极少数情况下,您可能会遇到带有旧版本 find 且没有此选项的旧系统)。知道-exec 存在很有用,但在可以删除文件的地方使用-delete。另外,除非您使用并理解-print0 选项,否则不要将| 的输出find 传递给另一个程序,否则当您遇到带有空格的文件时,您会受到伤害。
我明确包含的path/to/files 参数。如果您将其省略,它将隐式使用. 作为路径,但是明确说明路径会更安全(尤其是使用-delete)。
即使您说您使用的是 Linux 系统,我也会提到您将遇到的与 包括 Mac OS X 的 BSD implementations 的差异!对于其他系统(如旧的 Solaris 机器),祝你好运!升级到更现代的find 变体之一!
这个例子的主要区别在于正则表达式。 BSD 变体默认使用基本的 POSIX 正则表达式。为了避免basic-PRE 所需的正则表达式中繁重的额外转义,您可以通过在BSD 变体中指定-E 选项来利用扩展PRE 的更现代功能,以实现与使用-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,但我将举例说明如何或多或少地完成您的问题具体提出的内容(有一些注意事项)。
假设您使用具有 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 并通过 shopt -s extglob 开启了扩展通配符,那么您可以使用 Pattern Matching with Filename Expansion:
rm -f path/to/files/*.+(avi|mkv|wmv|mp4|mp5|flv|M4V|mpeg|mov|m1v|m2v|3gp|avchd)
+(pattern-list) 扩展的通配语法将匹配给定模式的一次或多次出现。
但是,我强烈建议不要使用rm,因为:
它容易出错且危险,因为很容易在* 之间放置一个空格,这意味着您将删除所有内容;您无法提前预览命令的结果;这是一劳永逸,祝你好运。
它是不可移植的,因为即使它恰好在您的特定 shell 中工作,相同的命令行也可能无法在其他 shell 中工作(包括其他 Bourne-shell 变体,如果您倾向于使用Bash 主义)。
它有严重的限制,因为如果您有嵌套在子目录中的文件,或者甚至在单个目录中有很多文件,那么在使用文件通配符时,您将很快达到命令行长度的限制.
我希望 rm 命令将只是 rm 本身被遗忘,因为我能想到的几个地方我宁愿使用 rm 而不是(甚至是古老的实现)find。
【讨论】: