首页 > 解决方案 > 仅通过文件名识别早于 x 个月的文件并删除它们

问题描述

我有 4 个具有不同 fileName.date 格式的不同文件,其中嵌入了一个日期作为名称的一部分。我只想根据名称来识别超过 3 个月的文件,因为这些文件稍后也会被编辑/更改。我想创建一个 shell 脚本并将其作为 cron 运行。下面是同一目录下的文件:

  1. fileone.log.2018-03-23
  2. file_two_2018-03-23.log
  3. 文件三.log.2018-03-23
  4. file_four_file_four_2018-03-23.log

我已经检查了现有的例子,但没有找到我真正想要的!

标签: bashshelldatesh

解决方案


在您的意思是 90 天的前提下工作 - 如果您需要特别的月份,我们也可以检查,但这是不同的逻辑。

这是您可以使用的一些代码-

(你说你不想从列表中工作,所以我编辑使用当前目录。)

$: cat chkDates
# while read f # replaced with -
for f in *[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*
do # first get the epoch timestamp of the file based on the sate string embedded in the name
   filedate=$(
      date +%s -d $(
         echo $f | sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/'
      ) # this returns the date substring
   )    # this converts it to an epoch integer of seconds since 1/1/70
   # now see if it's > 90 days ( you said 3 months. if you need *months* we have to do some more...)
   daysOld=$(( ( $(date +%s) - $filedate ) / 86400 )) # this should give you an integer result, btw
   if (( 90 < $daysOld ))
   then echo $f is old
   else echo $f is not
   fi
done # < listOfFileNames # not reading list now

您可以传递date一个日期来报告,并传递一个格式来呈现它。

sed模式解释

注意sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/'命令。这假设日期格式将是一致YYYY-MM-DD的,并且不验证合理性。它将愉快地接受任何 4 位数字,然后是 2,然后是 2,用破折号分隔。

-E使用扩展的正则表达式,因此括号()可以表示要记住的值,而不需要\'s. .表示任何字符,并且*表示前一个模式的任何数字(包括零),因此.*表示零个或多个字符,吃掉日期之前的所有行。[0-9]表示任何数字。{x,y}设置连续匹配的最小(x)和最大(y)数量 - 只有一个值{4}意味着只有前一个模式的 4 个可以做到。因此,'.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*'意味着忽略尽可能多的字符,直到看到 4 位数字,然后是破折号,2 位数字,然后是破折号,然后是 2 位数字;记住那个模式(()'s),然后忽略它后面的任何字符。

在替换中,\1表示第一个记住的匹配,所以

sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/'

意味着查找并记住文件名中的日期模式,并用输出中的那部分替换整个名称。这假设日期将存在 - 在没有日期的文件名上,模式将不匹配,并且将返回整个文件名,所以要小心。

(希望有帮助。)

通过使用(您的示例是格式一致的,所以我使用它)将日期字符串与文件名隔离开来sed,我们将其传入并使用 , 请求该日期字符串的 UNIX Epoch 时间戳date +%s -d $(...),以用数学方便的数字表示文件。

从相同格式的当前日期中减去它,您将获得文件的大致年龄(以秒为单位)。将它除以一天的秒数,你就得到了几天的时间。文件日期将默认为午夜,但数学会删除分数,所以它会整理出来。

这是我根据您的示例制作的文件列表

$: cat listOfFileNames
fileone.log.2018-03-23
fileone.log.2018-09-23
file_two_2018-03-23.log
file_two_2018-08-23.log
filethree.log.2018-03-23
filethree.log.2018-10-02
file_four_file_four_2018-03-23.log
file_four_file_four_2019-03-23.log

我为每个文件添加了一个文件,该文件将在发布后的 90 天内 - 包括一个“过期”的文件,这种情况很容易发生。

这是输出。

$: ./chkDates
fileone.log.2018-03-23 is old
fileone.log.2018-09-23 is not
file_two_2018-03-23.log is old
file_two_2018-08-23.log is not
filethree.log.2018-03-23 is old
filethree.log.2018-10-02 is not
file_four_file_four_2018-03-23.log is old
file_four_file_four_2019-03-23.log is not

这就是你的想法?

仅获取日期字符串的另一种纯 bash 方法

(您仍然需要date转换为纪元秒...)

代替

   filedate=$(
      date +%s -d $(
         echo $f | sed -E 's/.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*/\1/'
      ) # this returns the date substring
   )    # this converts it to an epoch integer of seconds since 1/1/70

这似乎不适合你,试试这个:

tmp=${f%[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*} # unwanted prefix
d=${f#$tmp}                                          # prefix removed
tmp=${f#*[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]} # unwanted suffix
filedate=${d%$tmp}                                   # suffix removed
filedate=$( date +%s --date=$filedate )              # epoch time

这很难阅读,但不必产生尽可能多的子流程来完成工作。:)

如果这不起作用,那么我怀疑你的date. 矿:

$: date --version
date (GNU coreutils) 8.26

推荐阅读