删除除第 12 个文件以外的所有文件

Dor*_*ina 14 bash rm

我有几千个格式为 filename.12345.end 的文件。我只想保留第 12 个文件,所以 file.00012.end、file.00024.end ... file.99996.end 并删除其他所有内容。

这些文件的文件名中也可能有数字,通常采用以下形式: file.00064.name.99999.end

我使用 Bash shell,无法弄清楚如何遍历文件,然后取出数字并检查它是否正在number%%12=0 删除文件。谁能帮我?

谢谢你,多丽娜

ter*_*don 18

这是一个 Perl 解决方案。对于数千个文件,这应该快得多:

perl -e '@bad=grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV; unlink @bad' *
Run Code Online (Sandbox Code Playgroud)

可以进一步浓缩为:

perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *
Run Code Online (Sandbox Code Playgroud)

如果您有太多文件并且无法使用 simple *,您可以执行以下操作:

perl -e 'opendir($d,"."); unlink grep{/(\d+)\.end/ && $1 % 12 != 0} readdir($dir)'
Run Code Online (Sandbox Code Playgroud)

至于速度,这是这种方法与其他答案之一中提供的外壳方法的比较:

$ touch file.{01..64}.name.{00001..01000}.end
$ ls | wc
  64000   64000 1472000
$ time for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done

real    2m44.258s
user    0m9.183s
sys     1m7.647s

$ touch file.{01..64}.name.{00001..01000}.end
$ time perl -e 'unlink grep{/(\d+)\.end/ && $1 % 12 != 0}@ARGV;' *

real    0m0.610s
user    0m0.317s
sys     0m0.290s
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,正如预期的那样,差异是巨大的。

解释

  • -e只是告诉perl运行命令行上给出的脚本。

  • @ARGV是一个特殊变量,包含给脚本的所有参数。由于我们给了它*,它将包含当前目录中的所有文件(和目录)。

  • grep会通过文件名列表中搜索和查找任何匹配的一串数字,点和end/(\d+)\.end/)

  • 因为数字 ( \d) 在一个捕获组(括号)中,所以它们被保存为$1. 因此,grep然后将检查号码是否是12的倍数,如果不是的话,文件名称将被退回。换句话说,该数组@bad包含要删除的文件列表。

  • 然后将列表传递给unlink()删除文件(但不是目录)。


Arr*_*cal 12

鉴于您的文件名格式为file.00064.name.99999.end,我们首先需要修剪掉除数字之外的所有内容。我们将使用一个for循环来做到这一点。

我们还需要告诉 Bash shell 使用基数 10,因为 Bash 算法会将它们以 0 开头的数字视为基数 8,这会给我们带来麻烦。

作为脚本,在包含文件的目录中启动时使用:

#!/bin/bash

for f in ./*
do
  if [[ -f "$f" ]]; then
    file="${f%.*}"
    if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
      rm "$f"
    fi
  else
    echo "$f is not a file, skipping."
  fi
done
Run Code Online (Sandbox Code Playgroud)

或者你可以使用这个非常长的丑陋命令来做同样的事情:

for f in ./* ; do if [[ -f "$f" ]]; then file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; else echo "$f is not a file, skipping."; fi; done
Run Code Online (Sandbox Code Playgroud)

解释所有部分:

  • for f in ./* 表示对于当前目录中的所有内容,执行...。这会将找到的每个文件或目录设置为变量 $f。
  • if [[ -f "$f" ]]检查找到的项目是否是一个文件,如果不是我们跳到该echo "$f is not...部分,这意味着我们不会意外地开始删除目录。
  • file="${f%.*}"将 $file 变量设置为删除最后一个..
  • if [[ $((10#${file##*.} % 12)) -eq 0 ]]是主要算术开始的地方。${file##*.}修剪.我们文件名中最后一个之前的所有内容,没有扩展名。$(( $num % $num2 ))是 Bash 算术使用模运算的语法,10#开头告诉 Bash 使用基数 10,来处理那些讨厌的前导 0。$((10#${file##*.} % 12))然后给我们留下文件名编号除以 12-ne 0的余数。检查余数是否“不等于”为零。
  • 如果余数不等于 0,则使用rm命令删除文件,您可能需要在第一次运行时替换rmecho,以检查您是否获得了要删除的预期文件。

此解决方案是非递归的,这意味着它只会处理当前目录中的文件,不会进入任何子目录。

if带有echo警告目录的命令的语句并不是真正必要的,因为rm它本身会抱怨目录,而不是删除它们,因此:

#!/bin/bash

for f in ./*
do
  file="${f%.*}"
  if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then
    rm "$f"
  fi
done
Run Code Online (Sandbox Code Playgroud)

或者

for f in ./* ; do file="${f%.*}"; if [[ $((10#${file##*.} % 12)) -ne 0 ]]; then rm "$f"; fi; done
Run Code Online (Sandbox Code Playgroud)

也会正常工作。

  • 调用 `rm` 几千次可能会很慢。我建议改为`echo` 文件名,并将循环的输出通过管道传送到`xargs rm`(根据需要添加选项):`for f in *; 做如果...; 然后回显“$f”;fi; 完成 | xargs -rd '\n' -- rm --`。 (5认同)

Nyk*_*kin 6

您可以使用 Bash 括号扩展来生成包含每 12 个数字的名称。让我们创建一些测试数据

$ touch file.{0..9}{0..9}{0..9}{0..9}{0..9}.end # create test data
$ mv file.00024.end file.00024.end.name.99999.end # testing this form of filenames
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用下面的

$ ls 'file.'{00012..100..12}* # print these with numbers less than 100
file.00012.end                 file.00036.end  file.00060.end  file.00084.end
file.00024.end.name.99999.end  file.00048.end  file.00072.end  file.00096.end
$ rm 'file.'{00012..100000..12}* # do the job
Run Code Online (Sandbox Code Playgroud)

尽管如此,对于大量文件来说,工作速度非常慢 - 生成数千个名称需要时间和内存 - 所以它更像是一个实际有效解决方案的技巧。