【问题标题】:Remove all files except some from a directory从目录中删除除某些文件之外的所有文件
【发布时间】:2011-05-18 12:50:09
【问题描述】:

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

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

【问题讨论】:

  • 听起来像是unix.stackexchange.com的问题
  • 有两种方法来阅读这个问题,现有的答案涵盖了这两种解释: 要么:(a)保留目标目录中具有指定名称的文件直接和- 正如rm -r 所暗示的那样 - 删除所有其他内容,包括子目录 - 即使它们包含具有指定名称的文件;或者:(b) 遍历目标目录的整个子树,并在每个目录中,删除除列出的名称之外的所有文件。
  • 对于所有这样做的人,请先备份。我刚刚浪费了几天的工作,因为我忘记排除 .git,并且没有推送,我无法恢复 30 多个提交。确保排除您关心的所有内容,包括隐藏文件夹。如果您正在处理目录,请设置-maxdepth 1

标签: bash rm


【解决方案1】:
find [path] -type f -not -name 'textfile.txt' -not -name 'backup.tar.gz' -delete

如果你不指定-type f find 也会列出你可能不想要的目录。


或者使用非常有用的组合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 组合。

【讨论】:

  • 看来 xargs 有文字大小限制
  • 删除多个模式:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm
  • 目录呢?它会删除所有文件,但它会删除文件夹吗?!
  • 而不是“| xargs rm”,find 需要一个 -delete 参数。
  • 而不是-print0 | xargs -0 rm -- 你可以-delete
【解决方案2】:
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

需要在 BASH 中启用 extglob(扩展模式匹配)(如果未启用):

shopt -s extglob

【讨论】:

  • 当我执行shopt -s extglob; rm -rf !(README|LICENSE) 时,我收到“意外标记 `(' 附近的语法错误”。知道为什么吗?
  • @nic 我不记得了,抱歉。我可能最终使用find-delete 选项。
  • 这对我来说是最好的解决方案,默认情况下它可以在我的 Ubuntu 12.04.4 LTS 上运行,无需 shopt
  • @nic, @Dennis:语法错误表明使用了 bash 以外的其他内容,例如 dash,其中不支持 extglob。但是,在 interactive bash shell 中,尽管出于不同的原因,该命令也不会像所述的那样工作。简而言之:自行执行shopt -s extglob;一旦成功启用(使用shopt extglob 验证),执行rm -rf !(README|LICENSE)。 (虽然extglob 尚未启用,但!(...) 在执行命令之前由历史扩展 评估;由于这可能会失败,因此不会执行任何命令,并且永远不会打开extglob。 )
  • @nic、@Dennis、@mklement0:在 .sh 文件 (#!当我在命令行界面中运行它时,结果发现除了在命令行界面中运行shopt -s extglob之外,我还必须在我的脚本文件中重新运行它。这为我解决了问题。
【解决方案3】:

find . | grep -v "excluded files criteria" | xargs rm

这将列出当前目录中的所有文件,然后列出所有与您的条件不匹配的文件(注意匹配目录名称),然后将其删除。

更新:根据您的编辑,如果您真的想从当前目录中删除除您列出的文件之外的所有内容,可以使用:

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(你有root权限,对吗?),将列出的文件移动到该目录,递归删除当前目录中的所有内容(你知道你在正确的目录中,是吗?),将/tmp_backup 中的所有内容移回当前目录,最后删除/tmp_backup

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

当然有更优雅的方法可以做到这一点,但这个方法非常简单。

【讨论】:

  • 与 egrep 一起工作也非常好,例如用于清理中间乳胶文件:find . | egrep -v "\.tex|\.bib" | xargs rm
  • 惊人的命令!删除目录只需更改为 rm -r
  • 如果您想简单地删除当前目录中除排除条件之外的所有内容:find . -maxdepth 1 | grep -v "exclude these" | xargs rm -r 工作得更快,因为它不需要不必要地深入目录。
  • Re find 管道:抛开效率问题(3 个命令用于 find 可以单独执行的操作),这对于嵌入空格的文件名将无法正常工作,并且可能会删除错误的文件.
  • 将“待保存”文件存储在另一个位置 (/tmp_backup) 的命令如果被中断,就不会很好地结束——从用户的角度来看,all这些文件已经消失了,除非他们知道去哪里寻找它们以取回它们。出于这个原因,我不赞成这种策略。
【解决方案4】:

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

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

-v, --invert-match 选择不匹配的行

\|分隔符

【讨论】:

  • 优雅的解决方案
  • 我最喜欢的解决方案 ;)
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

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

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

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

    像这样设置 GLOBIGNORE 会忽略通配符中的 php 和 sql,例如“ls *”或“rm *”。所以,设置变量后使用“rm *”只会删除txt和tar.gz文件。

    【讨论】:

    • +1;设置和恢复GLOBIGNORE 变量的更简单的替代方法是使用子shell:(GLOBIGNORE='*.php:*.sql'; rm *)
    【解决方案7】:

    因为没有人提到它:

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

    【讨论】:

    • 这更复杂,因为您必须在复制回来后处理权限。
    • @RemusAvram 您可以使用带有cprsync 的适当开关来保留权限。无论如何,这只是一种替代方法(作​​为建议给出),在这里有它的位置,作为对 OP 的回答。
    • 这可能不适合在许多情况下,其中要保留的文件正被其他进程积极使用。也很麻烦,要删除的文件只是一个小子集,涉及的文件很多。
    • @codeforester:当然。但是在某些情况下它是合适的,虽然......事实上我真的不明白你的评论:)的意义。
    【解决方案8】:

    你可以为此编写一个 for 循环... %)

    for x in *
    do
            if [ "$x" != "exclude_criteria" ]
            then
                    rm -f $x;
            fi
    done;
    

    【讨论】:

      【解决方案9】:

      对于 OP 来说有点晚了,但希望对任何后来通过谷歌到达这里的人有用...

      我发现@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 子句。

      【讨论】:

        【解决方案10】:

        如果您使用的是我强烈推荐的zsh

        rm -rf ^file/folder pattern to avoid
        

        extended_glob

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

        【讨论】:

          【解决方案11】:

          尝试使用它:

          rm -r !(Applications|"Virtualbox VMs"|Downloads|Documents|Desktop|Public)
          

          但是带有空格的名称(一如既往)很难。也尝试使用 Virtualbox\ VMs 而不是引号。它总是删除那个目录 (Virtualbox VMs)。

          【讨论】:

          • 不适用于文件。 rm !(myfile.txt) 删除所有包括myfile.txt
          • 应该像@pl1nk指出的那样首先执行shopt -s extglob
          • 是的,在 bash 中激活扩展通配符 (extglob) 非常重要,我在实验室中的几台 Mac 上每天都这样做:shopt -s extglob然后是cd /Users/alumno/,最后是rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) 了解扩展通配符here
          【解决方案12】:

          只是:

          rm $(ls -I "*.txt" ) #Deletes file type except *.txt
          

          或者:

          rm $(ls -I "*.txt" -I "*.pdf" ) #Deletes file types except *.txt & *.pdf
          

          【讨论】:

          • 不建议这样做。解析ls 输出可能会成为问题。有关详细信息,请参阅Here
          【解决方案13】:

          使文件不可变。甚至 root 也不允许删除它们。

          chattr +i textfile.txt backup.tar.gz script.php database.sql info.txt
          rm *
          

          所有其他文件已被删除。
          最终你可以将它们重置为可变的。

          chattr -i *
          

          【讨论】:

            【解决方案14】:

            我相信你可以使用

            rm -v !(filename)
            

            除了文件名之外的所有其他文件都将在目录中删除并确保您正在使用它

            【讨论】:

            • 使用下面的命令这对我有用。找 。 -类型 f \! -name '文件模式' -print0 | xargs --null rm -f
            • 此选项仅适用于启用 extglob shell 选项,即:$ shopt -s extglob
            【解决方案15】:

            这类似于@siwei-shen 的评论,但您需要-o 标志来执行多个模式。 -o 标志代表“或”

            find . -type f -not -name '*ignore1' -o -not -name '*ignore2' | xargs rm

            【讨论】:

              【解决方案16】:

              您可以使用两个命令序列来执行此操作。 首先用你不想排除的文件名定义一个数组:

              files=( backup.tar.gz script.php database.sql info.txt )
              

              之后,遍历你要排除的目录中的所有文件,检查文件名是否在你不想排除的数组中;如果不是,则删除该文件。

              for file in *; do
                if [[ ! " ${files[@]} " ~= "$file"  ]];then
                  rm "$file"
                fi
              done
              

              【讨论】:

              • 正则表达式比较不正确——它将保留名称是受保护文件之一的子字符串的任何文件(尽管周围的空格在一定程度上缓解了这种情况;但文件名可以包含空格)。您应该收集所有文件的数组,然后从该数组中减去要排除的文件,然后删除剩余的文件。
              【解决方案17】:

              由于还没有人提到这一点,在一个特殊情况下:

              OLD_FILES=`echo *`
              ... create new files ...
              rm -r $OLD_FILES
              

              (或只是rm $OLD_FILES

              OLD_FILES=`ls *`
              ... create new files ...
              rm -r $OLD_FILES
              

              如果某些文件可能存在或不存在,您可能需要使用shopt -s nullglob

              SET_OLD_NULLGLOB=`shopt -p nullglob`
              shopt -s nullglob
              FILES=`echo *.sh *.bash`
              $SET_OLD_NULLGLOB
              

              没有 nullglob,echo *.sh *.bash 可能会给你“a.sh b.sh *.bash”。

              (说了这么多,我个人更喜欢this answer,尽管它在OSX中不起作用)

              【讨论】:

              • Leonardo Hermoso 的现有答案用更少的错误来做到这一点。对于带有空格的文件名,这将失败;使用数组优雅地解决了这个特定问题(并且不太重要地避免使用大写作为他的私有变量,这通常是应该避免的)。
              【解决方案18】:

              请不要使用直接命令,而是将所需文件移动到当前目录之外的临时目录。然后使用rm *rm -r *删除所有文件。

              然后将需要的文件移动到当前目录。

              【讨论】:

              • 这可能不适合在许多情况下,其中要保留的文件正被其他进程积极使用。也很麻烦,要删除的文件只是一个小子集,涉及的文件很多。
              【解决方案19】:

              删除所有内容,不包括 file.name:

              ls -d /path/to/your/files/* |grep -v file.name|xargs rm -rf
              

              【讨论】:

              • 如果您的目录/文件有空格或不寻常的字符,这将严重失败并可能导致数据丢失。你永远不应该解析 ls。 mywiki.wooledge.org/ParsingLs
              猜你喜欢
              • 2010-10-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-07-31
              • 2023-03-28
              • 1970-01-01
              • 2014-09-06
              • 1970-01-01
              相关资源
              最近更新 更多