zoukankan      html  css  js  c++  java
  • shell编程入门

      最近由于工作原因,写了几个脚本,然后再linux中用定时任务去跑,记录一下shell脚本的学习过程吧!

      首先知道shell第一行#!/bin/bash表示这是一个shell脚本,例如下面这个简单的脚本:

    #!/bin/bash
    
    #author:java小新人
    
    #date:20200311
    
    #description:用于清理14天之前日志文件夹
    
    #这里就是要进行进行清理的nas中的目录
    baseDir="/usr/local/java/shellscript/store"
    
    #服务器中不存在该目录就退出
    if [ ! -d ${baseDir} ];then
      echo "不存在目录:${baseDir}"
      exit 1
    fi
    
    #这里指定前段时间第n天日志需要进行清理
    num=14
    
    #需要清理的日期转换成时间戳
    barrierDate=`date -d "$num days ago" +%F`
    barrierDateStamp=`date -d "${barrierDate}" +%s`
    
    #遍历目录下的所有日志文件进行正则匹配
    for file in `ls ${baseDir}`;do
      #获取目录中文件名中的日期
      dirDate=`expr "${file}" : '([0-9]{4}-[0-9]{2}-[0-9]{2})'`
      #目录中的日志文件名没有日期在其中,说明不需要删除,就跳过,进行下一次循环
      if [ -z "${dirDate}" ];then
        continue
      fi
      #获取该文件文件名的时间戳,当该时间符合需要清理的时间的时候,再判断一次该目录是否存在,保险一点!存在的话就删除就行了
      dirDateTimeStamp=`date -d "${dirDate}" +%s`
    
      if [ ${dirDateTimeStamp} -lt ${barrierDateStamp} ];then
        echo "当前遍历的目录是:${file},目录时间是:${dirDate}"
        #当前日志目录所在的绝对路径
        fileAbsPath="${baseDir}/${file}"
          if [ -d ${basePath} ];then
            echo "对${file}文件进行删除"
            rm -rf ${fileAbsPath}
            echo "日志目录删除成功:${fileAbsPath}"
          else
            echo "删除目录发生错误:${fileAbsPath}"
          fi
      fi
    done

      上面的目录中存放的就是每天生成的日志文件,粗略看一下上面这个简单的脚本,由于本人写脚本的水平抠脚,只能用这种很直白的写法,没有任何花里胡哨.....

      就是按照java代码那样的逻辑写的,应该都能看得懂,基本上是一些for循环和if判断;还有一个echo语句表示在命令行中输出相关信息,如果使用echo "xxx" >> /usr/local/file_name.log表示将echo输出的信息追加到/usr/local/file_name.log文件中;

      注意>和>>的区别,>是覆盖的意思,比如用命令清空一个文件,最简单的就是echo “” > a.txt;而>>表示在文件后面追加,不会覆盖

    1.先说说for循环

      下面这个就是遍历一个目录下的子目录的,不能遍历文件的哦!那个反引号,表示让系统去执行ls /etc命令,什么叫做让系统去执行呢?就跟我们手动敲这条命令去执行一样;还有一点,就是shell中要使用一个声明好的变量,需要用${xxx}表示,可以不加大括号,习惯加上好看点;

      最后就是要以done表示这个for循环结束;

    for file in `ls /etc`;do
        echo ${file}
    done

     2.if语句

      if和for写法差不多,下面这个作用是:当根目录下没有Top这个目录,就创建Top目录;注意,if后面的中括号那么多空格,不能少!!!if语句要以fi结尾;

      那么问题来了,那个-d是干嘛的呀?下面列出来了很多-xx的,写脚本的时候拿出来看一看就行;发现判断文件或者是字符串是不是空都可以用这种-xx的方式判断;

    if [ ! -d "/Top" ]; then
      mkdir -p /Top
    fi
    [ -a FILE ]  如果 FILE 存在则为真。  
    
    [ -b FILE ]  如果 FILE 存在且是一个块特殊文件则为真。  
    
    [ -c FILE ]  如果 FILE 存在且是一个字特殊文件则为真。  
    
    [ -d FILE ]  如果 FILE 存在且是一个目录则为真。  
    
    [ -e FILE ]  如果 FILE 存在则为真。  
    
    [ -f FILE ]  如果 FILE 存在且是一个普通文件则为真。  
    
    [ -g FILE ]  如果 FILE 存在且已经设置了SGID则为真。  
    
    [ -h FILE ]  如果 FILE 存在且是一个符号连接则为真。  
    
    [ -k FILE ]  如果 FILE 存在且已经设置了粘制位则为真。  
    
    [ -p FILE ]  如果 FILE 存在且是一个名字管道(F如果O)则为真。  
    
    [ -r FILE ]  如果 FILE 存在且是可读的则为真。  
    
    [ -s FILE ]  如果 FILE 存在且大小不为0则为真。  
    
    [ -t FD ]  如果文件描述符 FD 打开且指向一个终端则为真。  
    
    [ -u FILE ]  如果 FILE 存在且设置了SUID (set user ID)则为真。  
    
    [ -w FILE ]  如果 FILE 如果 FILE 存在且是可写的则为真。  
    
    [ -x FILE ]  如果 FILE 存在且是可执行的则为真。  
    
    [ -O FILE ]  如果 FILE 存在且属有效用户ID则为真。  
    
    [ -G FILE ]  如果 FILE 存在且属有效用户组则为真。  
    
    [ -L FILE ]  如果 FILE 存在且是一个符号连接则为真。  
    
    [ -N FILE ]  如果 FILE 存在 and has been mod如果ied since it was last read则为真。  
    
    [ -S FILE ]  如果 FILE 存在且是一个套接字则为真。  
    
    [ FILE1 -nt FILE2 ]  如果 FILE1 has been changed more recently than FILE2, or 如果 FILE1 exists and FILE2 does not则为真。  
    
    [ FILE1 -ot FILE2 ]  如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。  
    
    [ FILE1 -ef FILE2 ]  如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。  
    
    [ -o OPTIONNAME ]  如果 shell选项 “OPTIONNAME” 开启则为真。  
    
    [ -z STRING ]  字符串“STRING” 的长度为零则为真。 或者字符串为NULL时也为真。 
    
    [ -n STRING ]  和-z相反,默认不加-n也行,也就是说这个写法和[STRING]是一样的
    
    [ STRING1 == STRING2 ]  如果2个字符串相同。 “=” may be used instead of “==” for strict POSIX compliance则为真。  
    
    [ STRING1 != STRING2 ]  如果字符串不相等则为真。

     3.数组的遍历

      可以看到数组中每个元素用空格分隔就行了,如果是字符串数组也是一样的,例如:dirs=("/log1" "/log2");

      注意下面的for循环中有两个小括号啊,括号里面的${#arr(*)}表示数组里面元素的个数,注意和${arr[*]}的区别,这个表示数组中所有的实际元素

    arr=(1 2 3 4)
    for((i=0;${#arr[*])};i++));do
       echo ${arr[i]}
    done

      

      上面的for循环也可以用for in循环表示,,如下所示,看到这里应该很多人微微一笑,很多变成语言应该都知道这两种循环吧!

    arr=(1 2 3 4)
    for a in ${arr[*]};do
       echo ${a}
    done

     4.比较运算

      很多时候我们需要比较两个字符串是否相同,如下所示,注意,中括号两边的空格啊!==号两边空格,不要吝啬空格,不然你会发现一些奇葩的错误;

    if [ ${a} == ${b} ];then
        echo "相同"
    else
        echo "不相同"
    fi

       

      那么又有人要问了,数字的比较呢?注意,此时是没有大于号,小于号这种东西的(这里不用转义符号。。)

    左边等于右边: $a -eq $b;
    左边不等于右边: $a -ne $b;
    左边大于右边: $a -gt $b;
    左边小于右边: $a -lt $b;
    左边大于等于右边: $a -ge $b;
    左边小于等于右边: $a -le $b;

      这里注意一点东西,=,==和-eq都可以用来比较是否相等,都可以用在if后面的中括号中;在[ ]中=和==效果一样,在(( ))中=表示赋值,而==表示比较;那么eq和==的区别在哪呢?==可以比较字符串和数字,而eq只能比较数字(eq可以比较这样的字符串[ "20" -eq "20" ],不能比较[ "hello" -eq "hello" ],会报错),所以尽量用==;

    #!/bin/bash
    
    #不会报错,打印false
     if [ "a" == "" ];then
       echo "true"
     else
       echo "false"
     fi
    
    #会报错
     if [ "a" -eq "" ];then
       echo "true"
     else
       echo "false"
     fi

       另外,注意[[  ]]和[ ]的区别,简单说一下,只要你使用[[ ]]那么你想表示&&和||的关系,你可使用这两个符号,也可以使用对应的-a和-o;但是如果你使用的是[ ]那么你只能使用-a和-o,还只能在中括号里面,例如[ 5 -lt 3 ] -o [ 7 -gt 6 ]就会报错;

    if [[ 5 -lt 3 || 7 -gt 6 ]];then
      echo "true"
    else
      echo "false"
    fi
    
    
    if [[ 5 -lt 3 ]] || [[ 7 -gt 6 ]];then
      echo "true"
    else
      echo "false"
    fi
    
    
    if [ 5 -lt 3 -o 7 -gt 6 ];then
      echo "true"
    else
      echo "false"
    fi
    
    
    if [ 5 -lt 3 ] && [ 7 -gt 6 ];then
      echo "true"
    else
      echo "false"
    fi

     5.时间

      比较常见的就是脚本中处理时间:

    获取今天的日期:todayDate=`date -d now +%Y-%m-%d`或者`date +%F`
    明天日期:`date -d next-day +%Y-%m-%d`或者`date -d tomorrow +%Y-%m-%d`
    昨天日期:`date -d "1 years ago" +%Y-%m-%d`
    第n天前的日期:`date -d "n days ago" +%F`
    转换成时间戳:stamp=`date -d "${todayDate}" +s`

     6.正则匹配

      例如一个日志文件是这样的sgffg.log.2020-02-05.2,那么用下面这个正则去匹配,其中${file}表示该日志文件名;

    fileDate=`expr "${file}" : '.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*'`

     7.日志脚本

    #!/bin/bash
    
    #author:java小新人
    
    #date:20200218
    
    #description:主要用于定期备份过期日志
    
    #这里表示可以备份多个目录下的日志文件
    baseDirs=(
    "/home/path1" 
    "/home/path2" 
    "/home/path3"  
    )
    
    #想要备份的地方
    storePath="/home/store/path"
    
    #这里指定前段时间第n天日志需要进行备份
    num=7
    
    #备份成功文件的个数
    storeLogFileNum=0
    
    taskStartTime=`date "+%Y-%m-%d %H:%M:%S"`
    #因为需要把日志按照时间进行分类的,这里获取需要清理日志的那天日期以及时间戳
    barrierDate=`date -d "$num days ago" +%F`
    barrierDateStamp=`date -d "${barrierDate}" +%s`
    
    #路径不存在就创建该目录
    if [ ! -d ${storePath}/${barrierDate} ];then
      mkdir -p ${storePath}/${barrierDate} || exit 1
    fi
    
    #将脚本文件中echo输出的信息追加到文件中,这里该文件不存在就会创建
    storeRecord=${storePath}/${barrierDate}/storeRecord.log
    
    echo "当前时间为${taskStartTime},对日志文件时间是${barrierDate}的文件开始备份..." >> ${storeRecord}
    
    #开始遍历所有需要备份日志的目录
    for ((i=0;i<${#baseDirs[*]};i++));do
      basePath=${baseDirs[i]}
      
      #对存日志的路径判断进行处理,该目录不存在的话就进行下一次循环
      if [ ! -d ${basePath} ];then
        # echo "日志文件目录不存在,basePath:${basePath}"
        continue
      fi
      echo "开始对目录${basePath}下的日志文件进行处理" >> ${storeRecord}
    
      #判断日志目录下有没有日志文件
      folder=`ls ${basePath}`
      if [ -z ${folder} ];then
        echo "目录${basePath}中没有任何文件" >> ${storeRecord}
        continue
      fi
    
      #遍历目录下的所有日志文件进行正则匹配
      for file in ${folder};do
        #获取文件名中的日期
        fileDate=`expr "${file}" : '.*([0-9]{4}-[0-9]{2}-[0-9]{2}).*'`
        #目录中的日志文件不符合条件筛选条件,说明该文件不需要备份,跳过,进行下一次循环
        #这个很重要!!!!!!!
        if [ -z "${fileDate}" ];then
          continue
        fi
    
        #不为空就获取该文件文件名的时间戳,当该时间符合需要清理的时间的时候,就移动到目标目录中存起来
        fileDateTimeStamp=`date -d "${fileDate}" +%s`
    
        if [ ${fileDateTimeStamp} -eq ${barrierDateStamp} ];then
          #当前日志文件所在的绝对路径
          fileAbsPath="${basePath}/${file}"
          #如果在目标目录中有重名的,这里的-b参数会对目标目录中的同名文件进行备份,不会覆盖
          echo "对${file}文件进行备份" >> ${storeRecord}
          mv -b ${fileAbsPath} ${storePath}/${barrierDate}
          #日志文件移动成功的计数器
          ((storeLogFileNum++))
          echo "该文件备份成功" >> ${storeRecord}
        fi
      done
      echo "目录${basePath}下的日志文件处理完毕" >> ${storeRecord}
    done
    taskEndTime=`date "+%Y-%m-%d %H:%M:%S"`
    startSeconds=$(date --date="${taskStartTime}" +%s);
    endSeconds=$(date --date="${taskEndTime}" +%s);
    
    #执行该任务所花费时间,精确到秒
    runTime=$((endSeconds-startSeconds))"s"
    echo "日志备份整理完毕,备份完成时间为${taskEndTime},花费了${runTime},共备份了${storeLogFileNum}个日志文件" >> ${storeRecord}
    echo >> ${storeRecord}
    exit 0

      我这个脚本在linux定时任务中是每天执行一次,将前面第七天的日志备份,例如今天是3月8号清理3月1号的,3月9号清理3月2号的...,始终保证最新的一个星期的日志不被清理;

      备份之后生成的目录是这样的:

     8.定时任务

      我们肯定不会自己手动去执行这个shell脚本吧!这个时候就要用到linux的定时任务,最重要的时cron表达式;

      定时任务分两种,一种是系统级别的定时任务,可以通过vim /etc/crontab打开,但是不建议使用这个,这个文件必须要有root权限才能修改;另外一种是用户级别的定时任务,默认就是当前用户,使用crontab -e打开;

      那么问题来了,cron表达式怕写错了怎么办?肯定不会自己傻乎乎的等啊,这里有个在线的cron表达式测试工具https://tool.lu/crontab,可以直接看看你的任务啥时候执行;

      最后,注意一点,使用crontab -e打开之后,需要配置你的shell脚本绝对路径,例如0 1 * * * /usr/local/del_file.sh,注意哦!!!要给你的脚本添加可执行权限啊!

      chmod +x del_file.sh

  • 相关阅读:
    Linux下MySQL主从同步配置
    Tortoisegit图文使用教程
    C语言I博客作业06
    第十周助教总结
    C语言I博客作业04
    C语言I博客作业02
    第十一周助教总结
    第十二周助教总结
    第九周助教总结
    C语言I博客作业02
  • 原文地址:https://www.cnblogs.com/wyq1995/p/12466193.html
Copyright © 2011-2022 走看看