我有数千个类似这样的文件。
- wrfout_d03_2010-06-11_00:00:01
- wrfout_d03_2010-06-11_00:00:08
- wrfout_d03_2010-06-12_00:00:20
- wrfout_d03_2010-06-12_00:00:35
- wrfout_d03_2010-06-12_00:00:40
我只需要保留第一个时间戳。在这种情况下,
- wrfout_d03_2010-06-11_00:00:01
- wrfout_d03_2010-06-12_00:00:20
我可以知道这样做而不需要一一删除吗?谢谢!
答案1
和zsh
:
typeset -A seen=()
for f (wrfout_d*(N)) (( seen[\${f%_*}]++ )) && echo rm -f $f
echo
(如果对结果满意则删除)
等效bash
的(假设 bash 4.0 或更高版本)如下所示:
(shopt -s nullglob
typeset -A seen=()
for f in wrfout_d*; do
(( seen[\${f%_*}]++ )) && echo rm -f "$f"
done)
全局扩展按词法排序,因此使用时间戳格式,这确实与时间顺序一致。因此,上面我们从最旧到最年轻的顺序遍历文件,如果已经看到删除了最短尾随_*
( ) 的名称(如关联数组中记录的那样),则删除该文件。有关算术表达式中的原因,请参阅${f%_*}
$seen
A
\
如何在算术表达式中安全地使用关联数组?
答案2
prev=
for file in wrfout_d*_*_*; do
head=${file%_*}
if [ "$head" = "$prev" ]; then
# Remove "echo" if output is correct
echo rm -f -- "$file"
else
prev=$head
fi
done
文件名最后一个下划线之前的部分被视为变量head
。当与 相同的字符串echo rm
时达到,否则设置为 的值。head
prev
prev
head
答案3
无可否认,使用 bash 数组的脆弱解决方案:
#!/bin/bash
workdir='/home/haxiel/testdir'
prefixes=( $(ls $workdir | cut -d '_' -f 1-3 | sort | uniq) )
for prefix in ${prefixes[@]}; do
files=( $workdir/$prefix* )
unset files[0]
echo rm -- ${files[@]}
done
我正在使用ls|cut|sort|uniq
管道构建唯一前缀列表。
然后我循环遍历前缀并使用 shell 通配来获取与特定前缀匹配的所有文件并将其存储在数组中。您想保留第一个文件,因此我从数组中删除该文件并将其余文件传递给命令rm
。
此解决方案假设您的文件名不包含特殊字符。它还假设 shell 的排序顺序与您预期的排序顺序相匹配。
请务必将脚本放在工作目录之外。否则,脚本名称将被捕获为前缀之一。
运行一次并检查输出以确保您删除了正确的文件。然后,删除前面的'echo'命令rm
并再次运行它。
与往常一样,数据删除是一个有风险的过程,因此请谨慎使用,并在您认为需要时进行备份。