我有几千个格式为 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
的余数。检查余数是否“不等于”为零。rm
命令删除文件,您可能需要在第一次运行时替换rm
为echo
,以检查您是否获得了要删除的预期文件。此解决方案是非递归的,这意味着它只会处理当前目录中的文件,不会进入任何子目录。
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)
也会正常工作。
您可以使用 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)
尽管如此,对于大量文件来说,工作速度非常慢 - 生成数千个名称需要时间和内存 - 所以它更像是一个实际有效解决方案的技巧。
归档时间: |
|
查看次数: |
2241 次 |
最近记录: |