linux递归遍历并清理目录(2种方法)

方法一

shell脚本如下:

#!/bin/bash

#日志文件路径
logPath=/var/log/yx.log

echo -e "$(date +%Y%m%d" "%H:%M:%S): 清理程序开始执行!清理规则:A.文件大于100M,且存在时间超过5天;B.模型目录下的子目录存在时间超过30天
!\n"
echo -e "$(date +%Y%m%d" "%H:%M:%S): 清理程序开始执行!清理规则:A.文件大于100M,且存在时间超过5天;B.模型目录下的子目录存在时间超过30天!\n" >> $logPath

#计数器,计算总共删除了多少个目录
dirCnt=0
#计数器,计算总共删除了多少个文件
fileCnt=0

function recursive() {
  
  #dir="$1"
  #if [ "$dir" == "" ];then 
  #  echo 请输入一个参数,指定要清理的目录!
  #else
    #currDir=$1
    #判断参数$1,是否以 / 结尾
    #if [[ "$currDir" != */ ]];then
    #  #echo 当前目录/文件:${1%%/}
    #  currDir=$currDir"/"
    #fi

    echo -e "当前目录:$1"
    echo -e "当前目录:$1" >>$logPath
    #lines="`ls -l --time-style='+%Y%m%d' $1 | awk '{print $2,$6,$7,$1$8}'`"
    #while循环位于管道中,这意味着在运行过程中,while循环实际是位于一个新的SHELL中的,while循环中的变量和文件开头定义的变量是两个不同的变量,所以while循环中所改变的值在while循环结束后无法保存下来。解决这个问题的方法就是不要使用管道,改用重定向!
    while read line 
    do
      #echo $line
      #oldIFS=$IFS
      #IFS=" "  #设置字符串的分隔符为空格,默认为空格,所以这里不需要设置
      arr=($line)
      #echo "  ${#arr[@]}  ${arr[@]}"
      if [[ "${arr[0]:0:1}" == "d" ]];then
        #文件创建/修改日期距离当前日期的天数
        diffDays=$((($(date +%s) - $(date +%s -d ${arr[2]}))/86400))
        #判断当前目录是否是模型目录下的子目录,比如/data/xwd/20191102/,且存在时间超过30天,超过则删除
        if [[ "${arr[3]}" =~ ^[0-9]{4}(0[0-9]|1[12])([012][0-9]|3[01])$ && $diffDays -gt 30 ]];then
          rm -rf $1/${arr[3]}
          echo "    $1/${arr[3]}:该目录存在时间超过30天,删除之"
          echo "    $1/${arr[3]}:该目录存在时间超过30天,删除之" >> $logPath
          let dirCnt++
        #否则,递归处理下一个目录
        else    
          if [[ "$1" != */ ]];then
            recursive $1/${arr[3]}
          else
            recursive $1${arr[3]}
          fi
        fi
      elif [[ "${arr[0]:0:1}" == "-"  ]];then
        #echo "    $1/${arr[3]}"
        #如果文件存在天数大于60天,则删除(该条规则目前作废)
        #if [ $diffDays -gt 60 ];then
        #  echo "    $1/${arr[3]}:该文件存在时间超过60天,删除之"
        #  rm -f $1/${arr[3]}
        #  let fileCnt++

        #文件创建/修改日期距离当前日期的天数
        diffDays=$((($(date +%s) - $(date +%s -d ${arr[2]}))/86400))
        #如果文件大于100M(104857600字节),且存在时间超过5天,则删除
        if [[ ${arr[1]} -gt 104857600 && $diffDays -gt 5 ]];then
          rm -f $1/${arr[3]}
          echo "    $1/${arr[3]}:该文件大于100M,且存在时间超过5天,删除之"
          echo "    $1/${arr[3]}:该文件大于100M,且存在时间超过5天,删除之" >> $logPath
          let fileCnt++
        fi
      fi
    done <<< "`ls -l --time-style='+%Y%m%d' $1 | awk '{print $1,$5,$6,$7}'`"
  #fi
}

if [[ "$1" == "" ]];then
  echo -e "请输入一个参数,指定要清理的目录!\n"
  echo -e "请输入一个参数,指定要清理的目录!\n" >> $logPath
  exit 1
else
  recursive $1
fi

if [[ $dirCnt -eq 0 && $fileCnt -eq 0 ]];then
  echo -e "\n$(date +%Y%m%d" "%H:%M:%S): 清理程序执行完毕!没有要删除的文件!\n"
  echo -e "\n$(date +%Y%m%d" "%H:%M:%S): 清理程序执行完毕!没有要删除的文件!\n" >> $logPath
else
  echo -e "\n$(date +%Y%m%d" "%H:%M:%S): 清理程序执行完毕!共删除了 ${dirCnt} 个目录,和 ${fileCnt} 个文件!\n"
  echo -e "\n$(date +%Y%m%d" "%H:%M:%S): 清理程序执行完毕!共删除了 ${dirCnt} 个目录,和 ${fileCnt} 个文件!\n" >> $logPath
fi
exit 

方法二

#还未全部测试
#删除大于100M,且超过5天的文件
find . -size +100M -mtime +5 -type f -exec ls -alh {} \ >> yx.log;
find . -size +100M -mtime +5 -type f -exec rm -rf {} \;

#删除超过30天的文件夹
find /data -mindepth 2 -maxdepth 2 -type d -mtime +30 -exec ls -ahl15 {} \;
find /data -mindepth 2 -maxdepth 2 -type d -mtime +30 -exec rm -rf {} \;

一、过程中涉及的知识:

1、

查看当前目录的总大小

sudo du -sh .

查看当前目录下的所有子目录和子文件的总大小

sudo du -sh *

查看指定目录的总大小

sudo du -sh data/

2、普通的shell脚本使用以下语句作为第一行:

/bin/bash

3、修改文件/文件夹的修改时间:
touch -mt 1901020905 file1

4、ls 查看格式化后的时间:
ll --time-style='+%Y%m%d %H:%M:%S'
-rw-r----- 1 weblogic weblogic 5557932 20190102 09:05:00 aaa.txt

5、日期的格式化:

  • 特定日期转化为时间戳,即距离1970.01.01 08:00的秒数:
    $(date +%s -d '20120101') #结果为:1325347200
  • 当天和指定日期之间相隔的天数:
    diffDays=$((($(date +%s) - $(date +%s -d '20120101'))/86400))
  • 格式化当前日期:
    echo $(date +%Y%m%d" "%H:%M:%S) #结果是:20191205 13:56:24

6、单、双括号运算符:
shell单括号运算符号:
a=$(date);
等同于:a=date;
双括号运算符:
a=$((1+2));
echo $a;
等同于:
a=expr 1 + 2

7、awk知识:

  • 在awk中,输入文本的每一行中,由域分隔符分开的每一项称为一个域。通常,在不指名-F域分隔符的情况下,默认的域分隔符是空格。awk工作流程是这样的:读入有'\n'换行符分割的一条记录,然后将记录按指定的域分隔符划分域,填充域,$0则表示所有域,$1表示第一个域,$n表示第n个域。
  • -F ':' 指定输入值的域分隔符(field-separator)为英文冒号 :
    eg. ls -l --time-style='+%Y%m%d' $1 | awk '{print $1,$5,$6,$7}'
    在ls -l列出的文件列表信息中,打印第1、5、6、7个域

8、echo命令: -e 开启转义
eg. echo -e "请输入一个参数,指定要清理的目录!\n"
注: \n 表示换行符。

9、数组:

  • echo ${#arr[@]} #打印数组包含元素的个数
  • echo ${arr[@]} #打印数组的所有元素
  • echo ${arr[0]:0:1} #数组中某个元素从索引0开始截取1个字符,然后打印
  • shell 字符串转换为数组:
    line="a,b,c"
    oldIFS=$IFS
    IFS=, #设置字符串的分隔符为英文逗号,不设置的话默认为空格
    arr=($line)

10、shell中不能使用别名,如ll等。

11、vim中全局替换,其中包含了转义符号 \ :
:%s//var/log/yx.log/$logPath/g

二、过程中出现的错误:

1、Shell脚本报错:
/usr/local/bin/cleanData.sh: line 32: [: ==: unary operator expected
原因:在匹配字符串时用了类似这样的语句:
if[ $timeofday = "yes" ] ...
如果变量timeofday的值为空,那么就if语句就变成了if [ = "yes" ],这不是一个合法的条件。为了避免出现这种情况,我们必须给变量加上引号if [ "$timeofdat" = "yes" ],这样即使是空变量也提供了合法的测试条件,if [ " " = "yes" ]

2、Shell脚本报错:
cleanData.sh: line 63: [: missing `]'
其中,63行代码为:if [ ${arr[1]} -gt 104857600 && $diffDays -gt 7 ];then
原因:if使用了单个括号,需要使用双括号。
小结:网上还有一个错误原因是if ["$NGINX_FLAG" = '1'],"$NGINX_FLAG"之前和'1'之后没有空格,两个都要有空格。
另外,字符串的if语句比较最好使用双括号。他支持字符串的模式匹配,使用=~操作符时甚至支持shell的正则表达式。字符串比较时可以把右边的作为一个模式,而不仅仅是一个字符串,比如[[ hello == hell? ]],结果为真。[[ ]] 中匹配字符串或通配符,不需要引号。

posted @ 2021-08-25 17:00  mediocrep  阅读(1187)  评论(0编辑  收藏  举报
既然选择了远方,便只顾风雨兼程!