我有一个包含很多文件的目录。每个文件都有相同的模式<id>_data_<date>.csv
。我想要做的是删除所有文件,但保留最新的每个<id>
.
示例目录:
10020077_data_2017-07-18_001.csv
10020078_data_2017-07-18_001.csv
10020209_data_2019-04-23_001.csv
10020209_data_2019-04-24_001.csv
10020209_data_2019-04-25_001.csv
10020209_data_2019-04-26_001.csv
10020209_data_2019-04-27_001.csv
10020209_data_2019-04-28_001.csv
10020272_data_2019-04-23_001.csv
10020272_data_2019-04-24_001.csv
10020272_data_2019-04-25_001.csv
10020272_data_2019-04-26_001.csv
10020272_data_2019-04-27_001.csv
10020272_data_2019-04-28_001.csv
10020286_data_2019-04-23_001.csv
预期结果:
10020077_data_2017-07-18_001.csv
10020078_data_2017-07-18_001.csv
10020209_data_2019-04-23_001.csv <-- delete
10020209_data_2019-04-24_001.csv <-- delete
10020209_data_2019-04-25_001.csv <-- delete
10020209_data_2019-04-26_001.csv <-- delete
10020209_data_2019-04-27_001.csv <-- delete
10020209_data_2019-04-28_001.csv
10020272_data_2019-04-23_001.csv <-- delete
10020272_data_2019-04-24_001.csv <-- delete
10020272_data_2019-04-25_001.csv <-- delete
10020272_data_2019-04-26_001.csv <-- delete
10020272_data_2019-04-27_001.csv <-- delete
10020272_data_2019-04-28_001.csv
10020286_data_2019-04-23_001.csv
在这种情况下,我无法使用,find -mtime
因为有些 ID 每天都会收到新文件,而另一些 ID 则每月一次或有时每年一次。
我的想法是根据 id 对文件名进行分组,而不是保留最后一项。我该如何用 bash 解决这个问题?
答案1
这里没有特别需要 bash;一个简单的sh
脚本就可以做到这一点,利用位置数组两次。外循环获取所有所需的数据文件(id 和日期部分的通配符);它提取 id 部分,然后启动一个子 shell 来循环遍历具有该 id 的所有文件。然后,该子 shell 循环遍历这些文件的自然日期排序列表,并删除除最后一个文件之外的所有文件,保留最新的文件。
#!/bin/sh
set -- *_data_*.csv
for f in "$@"
do
id=${f%%_*}
# a subshell so we don't clobber $@
(
set -- "${id}"_data_*.csv
while [ "$#" -gt 1 ]
do
rm -- "$1"
echo "DELETE: $1"
shift
done
)
done
我添加了一条echo ... DELETE
语句,以便我可以演示您提供的文件名的结果:
DELETE: 10020209_data_2019-04-23_001.csv
DELETE: 10020209_data_2019-04-24_001.csv
DELETE: 10020209_data_2019-04-25_001.csv
DELETE: 10020209_data_2019-04-26_001.csv
DELETE: 10020209_data_2019-04-27_001.csv
DELETE: 10020272_data_2019-04-23_001.csv
DELETE: 10020272_data_2019-04-24_001.csv
DELETE: 10020272_data_2019-04-25_001.csv
DELETE: 10020272_data_2019-04-26_001.csv
DELETE: 10020272_data_2019-04-27_001.csv
答案2
您也可以使用一行命令序列来完成此操作,只要您的系统上有mktemp
、tee
、sort
、grep
、xargs
、 当然rm
可用。如果没有,tac
可以将其替换为sort -r
:
(temp_all=$(mktemp) && temp_last=$(mktemp) && { tac | tee $temp_all | sort -un > $temp_last ; } && grep -vf $temp_last $temp_all ; rm -f $temp_last $temp_all)
上面的内容需要 stdin 中的整个文件列表(它可以来自您认为合适的任何方式, a find
、 an ls
、 a 文件等)并显示要删除的文件列表。然后您可以将此类列表通过管道传输到xargs rm
细分:
(
temp_all=$(mktemp) && \
temp_last=$(mktemp) && \ # make a couple of temp files
{
tac | \ # reverse the list of files and ...
tee $temp_all | \ # pipe it into one temp entirely and also ...
sort -un > $temp_last ; \ # into a sort that makes names unique into the other temp
} && \
grep -vFf $temp_last $temp_all ; \ # use grep to filter out names
rm -f $temp_last $temp_all # remove temp files
)
这可以处理任意数量的输入名称,但要求没有名称嵌入换行符。对于你的情况来说这似乎是合理的。
答案3
Put all file names in l.txt
Proceed with below steps and it worked fine
da=`awk -F "_" '{print $3}' l.txt | sort | uniq| sort -nr| sed -n '1p'`
for id in `awk -F "_" '{print $3}' l.txt | sort | uniq`
> do
> find path -maxdepth 1 -type f -newermt $da -iname "$id*" | sed -n '2,$p'| awk '{print "rm" " " $1}'| sh;done
答案4
我知道已经有很多答案,但这里作为 Python 的替代方案。您不需要循环浏览文件两次。
#!/usr/bin/env python
import os
import glob
if __name__ == '__main__':
newest_dict = dict()
for f in glob.glob('*.csv'):
id = f[:8]
if id not in newest_dict:
newest_dict[id] = f
else:
nf = newest_dict[id]
f_ts = f[14:24]
nf_ts = nf[14:24]
if f_ts > nf_ts:
newest_dict[id] = f
print("Deleting", nf)
os.remove(nf)
else:
print("Deleting", f)
os.remove(f)