从目录中删除所有文件,除了某些文件。

229

使用 sudo rm -r 命令时,如何删除除以下文件外的所有文件:

textfile.txt
backup.tar.gz
script.php
database.sql
info.txt

5
听起来像是需要在 http://unix.stackexchange.com/ 上问一个问题。 - Jason
1
有两种方式可以阅读这个问题,现有的答案涵盖了这两种解释:要么(a)保留位于目标目录中指定名称的文件,并且-如同rm -r所暗示的那样-删除其他所有内容,包括子目录-即使它们包含具有指定名称的文件;或者(b)遍历目标目录的整个子树,在每个目录中删除除列出名称的文件之外的所有文件。 - mklement0
2
对于所有在执行此操作的人,请务必先备份。我刚因为忘记排除.git而浪费了数天的工作时间,由于没有推送,我无法恢复超过30次提交。确保您排除了所有您关心的内容,包括隐藏文件夹。如果你要处理目录,请设置"-maxdepth 1"。 - Lonami
@Jason [删除除一个文件外的所有文件/目录 - Unix&Linux Stack Exchange](https://unix.stackexchange.com/q/153862/209677) - Pablo Bianchi
20个回答

241
find [path] -type f -not -name 'textfile.txt' -not -name 'backup.tar.gz' -delete

如果您不指定 -type ffind 命令也会列出目录,而这可能并不是您想要的。


或者使用非常有用的组合 find | xargs 的更一般的解决方案:

find [path] -type f -not -name 'EXPR' -print0 | xargs -0 rm --
例如,在当前目录中删除所有非 txt 文件:
find . -type f -not -name '*txt' -print0 | xargs -0 rm --

如果要删除文件名中包含空格的文件,就需要使用print0-0组合。


2
似乎xargs有文本大小限制。 - frazras
26
删除多个文件模式:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm - Siwei
5
目录呢?它会删除所有文件,但它会移除文件夹吗?! - orezvani
33
可以使用find的-delete参数代替“| xargs rm”。 - Emil Stenström
1
你可以使用-delete代替-print0 | xargs -0 rm -- - Andy
显示剩余5条评论

157
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

需要在BASH中启用扩展模式匹配(Extended Pattern Matching)的extglob(扩展通配符),如果它没有被启用:
shopt -s extglob

3
当我执行 shopt -s extglob; rm -rf !(README|LICENSE) 时,出现 "syntax error near unexpected token `('" 错误提示。你知道是为什么吗? - Dennis
@nic,抱歉我不记得了。我可能最终使用了带有“-delete”选项的“find”。 - Dennis
3
@nic,@Dennis:这个语法错误表明使用了除bash之外的其他东西,比如dash,在那里不支持extglob。然而,在一个交互式的bash shell中,该命令也不会按照所述方式工作,尽管原因不同。简而言之:执行shopt -s extglob;一旦成功启用(使用shopt extglob验证),则执行rm -rf !(README|LICENSE)。(当extglob尚未启用时,!(...)将由历史扩展评估,然后再执行命令;由于这可能会失败,因此没有任何一个命令被执行,而且extglob从未被打开。) - mklement0
3
@nic,@Dennis,@mklement0:当我在 .sh 文件(#! /bin/bash)中执行命令时,出现了“syntax error near unexpected token ("”的问题,但是在命令行界面中运行时却没有问题。后来发现除了在命令行界面中运行 shopt -s extglob 之外,还要在我的脚本文件中重新运行它。这对我解决了问题。 - Ahresse
如果有人想要合并它,这并不总是一个好主意。rm -rf * .* !(src/|lost+found/) <-- 它没有按照我的预期工作,它删除了所有东西 ;) - Qback
显示剩余4条评论

43

find . ! -name "file1" ! -name "file2" -type f -exec rm {} +

This command finds all files in the current directory that are not named "file1" or "file2", and deletes them.

mkdir /tmp_backup && mv textfile.txt backup.tar.gz script.php database.sql info.txt /tmp_backup/ && rm -r && mv /tmp_backup/* . && rmdir /tmp_backup

这将创建一个备份目录 /tmp_backup(您拥有根权限,对吧?),将您列出的文件移动到该目录中,递归删除当前目录中的所有内容(您知道您在正确的目录中,对吗?),然后将来自 /tmp_backup 的所有内容移回到当前目录中,并最终删除 /tmp_backup

我选择将备份目录放在根目录下,因为如果您试图递归地从根目录中删除所有内容,系统将会出现大问题。

当然还有更优雅的方法来完成此操作,但这种方法非常直接。


2
使用egrep也可很好地工作,例如用于清理中间的latex文件:find . | egrep -v "\.tex|\.bib" | xargs rm - mtsz
惊人的命令!只需切换到rm -r即可删除目录。 - Aris
1
如果您想要删除当前目录中除了排除标准之外的所有内容:find . -maxdepth 1 | grep -v "exclude these" | xargs rm -r 可以更快地工作,因为它不需要不必要地深入到目录中。 - But those new buttons though..
关于“查找(find)”流程:除了效率问题之外(使用3个命令完成find单独可以完成的工作),如果文件名中包含空格,这将无法按预期工作,并有可能误删文件。 - mklement0
如果一个命令将“待保存”的文件存储在另一个位置(/tmp_backup),但是在执行过程中被中断,那么从用户的角度来看,这个命令并不会很好地结束,除非他们知道去哪里寻找这些文件以便恢复它们。因此,我不赞成这种策略。 - Craig McQueen

25

我更喜欢使用子查询列表:

rm -r `ls | grep -v "textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt"`

-v, --invert-match 选择不匹配的行 \| 分隔符
为了避免保留具有相似名称的文件:
rm -r `ls | grep -v "^textfile.txt$\|^backup.tar.gz$"`

1
优雅的解决方案 - Joshua Salazar
1
我最喜欢的解决方案 ;) - Kyrol
1
这真的很容易出错。例如,它会保留名为 old-textfile.txt 的文件等。 - istepaniuk
@istepaniuk,说得好!我已经更新了一个高级用法。 - Nick Tsai
如果我想反过来做,比如保留匹配的文件怎么办?去掉-v会显示rm: missing operand - undefined

20

假设在目录树的多个位置中存在具有这些名称的文件,并且您想保留所有这些文件:

find . -type f ! -regex ".*/\(textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt\)" -delete

19

你可以在Bash中使用GLOBIGNORE环境变量。

假设你想删除除了php和sql之外的所有文件,那么你可以这样做:

export GLOBIGNORE=*.php:*.sql
rm *
export GLOBIGNORE=

设置GLOBIGNORE为这样可以忽略通配符中的php和sql,例如"ls *"或"rm *"。因此,在设置该变量后使用"rm *"将仅删除txt和tar.gz文件。


8
一个比设置和恢复 GLOBIGNORE 变量更简单的替代方法是使用子 shell:(GLOBIGNORE='*.php:*.sql'; rm *)。该命令会在子 shell 中设置 GLOBIGNORE 变量为 '*.php:*.sql' 并删除所有文件。 - mklement0

9

由于没有人提到它:

  • 将您不想删除的文件复制到安全位置
  • 删除所有文件
  • 将复制的文件移回原位

2
这更加复杂,因为你必须在将它们复制回去后注意权限。 - Romulus
2
@RemusAvram 你可以使用适当的开关来保留 cprsync 的权限。无论如何,这只是一种备选方法(作为建议提供),在这里有其存在的价值,作为对 OP 的答案。 - gniourf_gniourf
在许多情况下,这可能不合适,因为要保留的文件正在被其他进程使用。如果要删除的文件只是一个小子集,并且涉及大量文件,则这也很麻烦。 - codeforester
@codeforester:当然可以。但是有些情况下使用它是合适的,实际上我并不明白你的评论的意义“:)”。 - gniourf_gniourf

7
你可以使用for循环来完成这个任务... %)
for x in *
do
        if [ "$x" != "exclude_criteria" ]
        then
                rm -f $x;
        fi
done;

6
虽然有点晚了,但希望对通过Google搜索到这里的任何人有所帮助...
我发现@awi和@Jamie Bullock关于-delete的评论非常有用。使用简单的实用程序,在不同的目录中进行此操作时,每次忽略不同的文件名/类型,只需最少的输入即可完成:
rm_except(或任何您想命名的名称)
#!/bin/bash

ignore=""

for fignore in "$@"; do
  ignore=${ignore}"-not -name ${fignore} "
done

find . -type f $ignore -delete

例如,删除除文本文件和foo.bar之外的所有内容:
rm_except *.txt foo.bar 

类似于 @mishunika 的方式,但没有 if 语句。

5
如果你正在使用我强烈推荐的zsh
rm -rf ^file/folder pattern to avoid

使用extended_glob
setopt extended_glob
rm -- ^*.txt
rm -- ^*.(sql|txt)

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