Linux日志分割脚本

该脚本的功能是定时分割日志文件

#!/usr/bin/env bash

#定时分割清理日志文件

#usage="Usage: logrotate (start|stop) file (line|size)"
#usage="-e Usage: logrotate start file (line|size) or logrotate stop"
usage="-e Usage: ${PROGRAM} { start file (line|size) | stop | restart file (line|size) | status | version | help }"

#获取该文件的名称
declare -r PROGRAM="$(basename "$0")"

#解析参数
# get arguments
startStop=$1
shift
logfile=$1
shift
lineSize=$1
shift
#echo $startStop
#echo "logfile: $logfile"

#设置心跳,即多长时间执行一次,单位s
HEARTBEAT=${HEARTBEAT:-$[1*60*60]}
#HEARTBEAT=30
#echo $HEARTBEAT

#日志保存天数
SAVE_DAYS=${SAVE_DAYS:-15}

function get_time(){
  #获取今天日期
  TODAY_DATE=$(date +%Y%m%d)
  #echo $TODAY_DATE

  #获取 $SAVE_DAYS 天前的日期
  #EARLY_DATE=`date +%Y%m%d --date="-1 day"`
  EARLY_DATE=$(date +%Y%m%d --date="-${SAVE_DAYS} day")
  #echo $EARLY_DATE

  #获取当前时间
  cur_time=$(date "+%Y%m%d%H%M%S")
}

#清理过期日志文件
function delete_expired_logfile() {
  logfileLength=`echo ${FILEPrefix} |wc -L`
  let logfileLength=$logfileLength+24
  #echo $logfileLength
  
  #遍历符合表达式的文件
  for logfiles in `ls ${FILEDIR} | grep -o -E ${FILEPrefix}_'[0-9]*'_'[0-9]*'.log`
  do
    #echo $logfiles
    logfileLength2=`echo ${logfiles} |wc -L`
    #如果文件长度等于规定的长度,
    if [ $logfileLength2 -eq $logfileLength ] ; then
      #获取该日志文件的日期
      logDate=${logfiles:6:8}
      if [ $logDate -lt $EARLY_DATE ] ; then
        rm -rf ${FILEDIR}/$logfiles
      fi
    fi
  done
}

#获取日志目录与日志名
function get_logname_info() {
  #logfile=/home/user/file/out.log
  #已知文件,获取文件名,该方式是从左开始最大化匹配到字符"/",然后截掉左边内容(包括字符"/"),返回余下右侧部分。
  FILENAME=${logfile##*/}
  #echo "filename: $FILENAME"
  
  #判断$logfile是否包含"/"
  #echo "$logfile" |grep -q "/"
  #if [ $? -eq 0 ] ; then
  if [[ $logfile =~ "/" ]] ; then
    #已知文件,获取文件路径,该方式是从右开始第一次匹配到字符"/",然后截掉右边内容(包括字符"/"),返回余下左侧部分。
    FILEDIR=${logfile%/*}
  else
    FILEDIR="."
  fi

  #echo "filedir: $FILEDIR"

  #获取文件名前缀
  FILEPrefix=${FILENAME%.*}
  #echo "filePrefix: $FILEPrefix"

  #判断$FILENAME是否包含"."
  if [[ $FILENAME =~ "." ]] ; then
    #获取文件名后缀
    FILESuffix=${FILENAME##*.}
  else
    FILESuffix=""
  fi
  #echo "fileSuffix: $FILESuffix"

  #LOG_DIR="/usr/local/nginx/logs/"
  #echo $LOG_DIR
}

###################################
#(函数)判断程序是否已启动
#
#说明:
#使用JDK自带的JPS命令及grep命令组合,准确查找pid
#jps 加 l 参数,表示显示java的完整包路径
#使用awk,分割出pid ($1部分),及Java程序名称($2部分)
###################################
#初始化psid变量(全局)
psid=0

function checkpid() {
   javaps=`ps -ef | grep $PROGRAM | grep -v "grep"`

   if [ -n "$javaps" ]; then
      psid=`echo $javaps | awk '{print $2}'`
   else
      psid=0
   fi
}

###################################
#(函数)启动程序
#
#说明:
#1. 首先调用checkpid函数,刷新$psid全局变量
#2. 如果程序已经启动($psid不等于0),则提示程序已启动
#3. 如果程序没有被启动,则执行启动命令行
#4. 启动命令执行后,再次调用checkpid函数
#5. 如果步骤4的结果能够确认程序的pid,则打印[OK],否则打印[Failed]
#注意:echo -n 表示打印字符后,不换行
#注意: "nohup 某命令 >/dev/null 2>&1 &" 的用法
###################################
function do_start() {
  echo -n "Starting $PROGRAM ..."
  checkpid
  echo -n "(pid=$psid) "
  if [ $psid -ne 0 ]; then
    echo "[OK]"
    if [[ $lineSize == "line" ]] || [[ $lineSize == "size" ]] ; then
      while true
      do
        get_time
        delete_expired_logfile
        case "${lineSize:-}" in
          line)
            #divide_file_by_line
            divide_file_by_line2
            ;;

          size)
            divide_file_by_size
            ;;

        esac
      
        sleep ${HEARTBEAT}
      done
    else
      echo "Usage: logrotate start file (line|size)"
    fi
  else
    echo "[Failed]"
  fi
}

###################################
#(函数)停止程序
#
#说明:
#1. 首先调用checkpid函数,刷新$psid全局变量
#2. 如果程序已经启动($psid不等于0),则开始执行停止,否则,提示程序未运行
#3. 使用kill -9 pid命令进行强制杀死进程
#4. 执行kill命令行紧接其后,马上查看上一句命令的返回值: $?
#5. 如果步骤4的结果$?等于0,则打印[OK],否则打印[Failed]
#6. 为了防止java程序被启动多次,这里增加反复检查进程,反复杀死的处理(递归调用stop)。
#注意:echo -n 表示打印字符后,不换行
#注意: 在shell编程中,"$?" 表示上一句命令或者一个函数的返回值
###################################
function do_stop() {
  checkpid

  if [ $psid -ne 0 ]; then
    echo -n "Stopping $PROGRAM ...(pid=$psid) "
    kill -9 $psid
    if [ $? -eq 0 ]; then
      echo "[OK]"
    else
      echo "[Failed]"
    fi

    checkpid
    if [ $psid -ne 0 ]; then
      stop
    fi
  else
    echo "================================"
    echo "warn: $PROGRAM is not running"
    echo "================================"
  fi
}

#copy文件
function copy_logfile() {
  logfile_bak=${logfile}.bak
  #echo ${logfile_bak}
  cp -rf $logfile ${logfile_bak}
  #filename_bak=${FILENAME}.bak
  #echo ${FILENAME}
  #echo ${filename_bak}
  #cp -rf ${FILEDIR}/${FILENAME} ${FILEDIR}/${filename_bak}
}

#设置划分文件的行数
LINE_NUM=${LINE_NUM:-30000}
#LINE_NUM=${LINE_NUM:-3}
#echo $LINE_NUM
#按照固定行数分割文件
function divide_file_by_line() {
  #get_time

  #获取日志文件的行数
  #line=`cat $logfile | wc -l`
  line=`sed -n '$=' $logfile`
  #echo $line
  startLine=1
  #endLine=$line
  i=0
  # 如果line > LINE_NUM,则进行划分, (-gt > , -lt <, )
  #while [ ${endLine} -gt 0 ]
  while [ ${startLine} -lt ${line} ]
  do
    #startLine=1
    let endLine=$startLine+$LINE_NUM-1
    num=`printf "%04d\n" $i`

    #sed -n "${startLine},${endLine} p;q" $logfile >result_${cur_time}.txt
    #将 $logfile 文件的从${startLine}到${LINE_NUM}行的数据写到 文件
    #sed -n "${startLine},${LINE_NUM} p" $logfile >${FILEPrefix}_${cur_time}_${num}.log
    sed -n "${startLine},${endLine} p" $logfile >${FILEDIR}/${FILEPrefix}_${cur_time}_${num}.log
    #删除指定的行
    #sed -i "${startLine},${LINE_NUM} d" $logfile
    #let endLine=${endLine}-${LINE_NUM}
    let startLine=$startLine+$LINE_NUM
    let i=$i+1
    #sleep 1s
  done
  #清空文件内容,因为日志文件实时更新,所以不能清空
  #echo "" > $logfile
  #删除指定的行
  sed -i "1,${line} d" $logfile
}

#按照固定行数分割文件
function divide_file_by_line2() {
  #get_time

  #获取日志文件的行数
  line=`cat $logfile | wc -l`
  #line=`sed -n '$=' $logfile`

  # 以行为单位划分文件,-l行数,将urls.txt文件按2000行一个文件分割为多个文件;-d添加数字后缀,比如00,01,02;-a 2表示用两位数据来顺序命名;url_定义分割后的文件名前缀。
  # split urls.txt -l 2000 -d -a 2 url_
  split $logfile -l ${LINE_NUM} -d -a 4 "${FILEDIR}/${FILEPrefix}_${cur_time}_"
  #ls|grep tmp_|xargs -n1 -i{} mv {} {}.txt,意思是先查找tmp_开头的文件,然后逐个重命名为.txt
  ls ${FILEDIR} |grep "${FILEPrefix}_${cur_time}_"|xargs -n1 -i{} mv ${FILEDIR}/{} ${FILEDIR}/{}.log
  #rename_logfile

  #清空文件内容,因为日志文件实时更新,所以不能清空
  #echo "" > $logfile
  #删除指定的行
  sed -i "1,${line} d" $logfile
}

#设置划分文件的大小(单位MB)(1MB=1*1024KB=1*1024*1024B=1*1024*1024*8b)
#转换成KB
#SIZE=${SIZE:-$[32*1024]}
SIZE=${SIZE:-32}
#SIZE=5
#echo $SIZE
#按照文件大小,分割文件
function divide_file_by_size() {
  #get_time

  #获取日志文件的行数
  line=`cat ${logfile} | wc -l`
  
  #按文件大小划分
  # 以文件大小为单位划分文件,-b, --bytes=SIZE:对file进行切分,每个小文件大小为SIZE,可以指定单位b,k,m;-d添加数字后缀,比如00,01,02;-a 2表示用两位数据来顺序命名;url_定义分割后的文件名前缀。
  # split urls.txt -b 500k -d -a 2 url_
  split $logfile -b ${SIZE}m -d -a 4 "${FILEDIR}/${FILEPrefix}_${cur_time}_"
  #ls|grep tmp_|xargs -n1 -i{} mv {} {}.txt,意思是先查找tmp_开头的文件,然后逐个重命名为.txt
  ls ${FILEDIR}|grep "${FILEPrefix}_${cur_time}_"|xargs -n1 -i{} mv ${FILEDIR}/{} ${FILEDIR}/{}.log
  #rename_logfile

  #删除指定的行
  sed -i "1,${line} d" $logfile
}

#重命名日志文件
function rename_logfile() {
  #加.log后缀
  for logfiles in `ls ${FILEDIR} | grep "${FILEPrefix}_${cur_time}_"`
  do
    # 拼接成文件名
    logfilename="${logfiles}.log"
    # 更改文件名
    mv ${FILEDIR}/$logfiles ${FILEDIR}/$logfilename
  done
}

function do_version() {
  echo "$PROGRAM version \"1.0\""
}

###################################
#(函数)检查程序运行状态
#
#说明:
#1. 首先调用checkpid函数,刷新$psid全局变量
#2. 如果程序已经启动($psid不等于0),则提示正在运行并表示出pid
#3. 否则,提示程序未运行
###################################
status() {
   checkpid

   if [ $psid -ne 0 ];  then
      echo "$PROGRAM is running! (pid=$psid)"
   else
      echo "$PROGRAM is not running"
   fi
}

#主函数
function main() {
  get_logname_info

  case "${startStop:-}" in
    start)
      if [ ${logfile:-} ] ; then
        do_start
      else
        echo "Usage: logrotate start file (line|size)"
      fi
      ;;

    stop)
      do_stop
      ;;

    restart)
      do_stop
      if [ ${logfile:-} ] ; then
        do_start
      else
        echo "Usage: logrotate restart file (line|size)"
      fi
      ;;

    status)
      do_status
      ;;

    version|--version|-v)
      do_version
      ;;

    help|--help|-help)
      echo $usage #"Usage: ${PROGRAM} { start | stop | restart | status | version | help }"
      ;;

    *)
      echo >&2 $usage #"Usage: ${PROGRAM} { start | stop | restart | status | version | help }"
      exit 1
      ;;
  esac
}

#运行
main "$@"

 

posted @ 2018-10-16 11:42  秦时明月0515  阅读(2160)  评论(0编辑  收藏  举报