删除文件 - 根据文件名模式保留最新文件

删除文件 - 根据文件名模式保留最新文件

我有一个包含很多文件的目录。每个文件都有相同的模式<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

您也可以使用一行命令序列来完成此操作,只要您的系统上有mktempteesortgrepxargs、 当然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)

相关内容