Linux系統中有日誌切割的工具logrotate,它可以按照我們的預期按時間或者大小來切割和歸檔老的日誌,它還可以壓縮切割後的日誌,也可以定義老日誌保留的時間。
本案例的需求是用shell腳本來實現類似logrotate的功能,具體要求如下:
1)要處理的日誌路徑:/data/logs/1.log
2)每天0點0分切割日誌
3)老日誌保留一週
4)歸檔後的日誌名字爲:1.log.1,1.log.2,... ,1.log.7
5)假設日誌歸檔後,新日誌可以自動生成
知識點一:logrotate
先聲明,本案例腳本並不會引用該部分知識點,在這裏僅做簡單介紹,下面通過一段配置解釋其含義。
/var/log/messages { //日誌的路徑 rotate 5 //保留5份歸檔後的日誌 weekly //一週切割一次 postrotate //定義切割日之後,要執行的指令 /usr/bin/killall -HUP syslogd //重載syslogd服務,這樣會生成新的/var/log/messages文件 endscript //表示執行shell命令到此結束 } "/var/log/httpd/access.log" /var/log/httpd/error.log { //同時寫多個日誌的路徑 rotate 5 mail [email protected] //把歸檔後的日誌發送給該郵箱 size 100k //當日志文件達到100k時開始做切割 sharedscripts //對於第一行定義的所有日誌來說,以下的shell命令(/usr/bin/killall -HUP httpd)只運行一次,而不是每切割一個日誌就執行一次。 postrotate /usr/bin/killall -HUP httpd endscript } /var/log/news/* { //支持*通配 monthly //一個月切割一次日誌 rotate 2 //保留2個歸檔日誌 olddir /var/log/news/old //將切割後的日誌保存到這個目錄下面 missingok //如果日誌文件丟失,不報錯 postrotate kill -HUP 'cat /var/run/inn.pid' endscript nocompress //歸檔後的日誌不壓縮,與其相對的參數是compress }
知識點二:shell中的exit、break和continue
1)exit
在shell腳本中使用exit,則直接退出當前腳本,exit後續的所有指令都不再執行,示例腳本exit.sh:
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i -eq 3 ] then exit 1 fi echo $i done echo aaaaa
執行腳本,結果如下:
# sh exit.sh 1 1 2 2 3
exit後面可以跟一個數字,表示該腳本的返回值,執行完腳本後,執行echo $?可以打印腳本的返回值。
# echo $? 1
2)break
在shell腳本的循環中,可以使用break跳出循環體,示例腳本break.sh:
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i -eq 3 ] then break fi echo $i done echo aaaaa
執行腳本,結果如下:
# sh break.sh 1 1 2 2 3 aaaaa
說明:當$i爲3時,遇到break指令,此時會跳出整個循環,從而執行echo aaaaa指令。
3)continue
和break類似,continue的作用是結束本次循環,繼續下一次循環,而不會結束整個循環體,示例腳本continue.sh:
#!/bin/bash for i in `seq 1 5` do echo $i if [ $i -eq 3 ] then continue fi echo $i done echo aaaaa
執行腳本,結果如下:
# sh continue.sh 1 1 2 2 3 4 4 5 5 aaaaa
說明:當$i=3時,遇到continue指令,此時結束本次循環,即continue後面的指令echo $i不再執行,繼續後續循環,即i=4。
本案例參考腳本
#!/bin/bash #日誌切割歸檔,按天切割,1.log變1.log.1, 1.log.1變1.log.2, ... #作者: #日期: logdir=/data/logs/ #定義函數,如果一個文件存在,則刪除 function e_df() { if [ -f $1 ] then rm -f $1 fi } cd $logdir #從7到2,依次遍歷循環 for i in `seq 7 -1 2` do #$i2比$i小1 i2=$[$i-1] #首先判斷1.log.7是否存在,若存在則刪除 e_df 1.log.$i #當1.log.6存在,則把1.log.6改名爲1.log.7,依次類推 if [ -f 1.log.$i2 ] then mv 1.log.$i2 1.log.$i fi done #由於1.log後面無後綴,所以不能走上面的for循環,只能另外拿出來處理 e_df 1.log.1 mv 1.log 1.log.1 ##說明:這個腳本寫完後,放到任務計劃裏,每天0點0分執行。
需求擴展
現在的需求是,日誌按大小來切割,比如當日志大於等於100M時,則需要處理,處理後的日誌需要壓縮,並且延遲一天,即1.log.1不用壓縮,其他需要壓縮。
#!/bin/bash #日誌切割歸檔,按日誌大小切割(100M),1.log變1.log.1, 1.log.1變1.log.2, ... #作者: #日期: logdir=/data/logs/ #技術1.log大小 size=`du -sk $logdir/1.log |awk '{print $1}` #如果1.log小於100M,則退出腳本 if [ $size -lt 10240 ] then exit 0 fi #定義函數,如果一個文件存在,則刪除 function e_df() { if [ -f $1 ] then rm -f $1 fi } cd $logdir #如果1.log.1存在,則先把它壓縮爲1.log.1.gz,這樣下面的for循環纔不會出錯 if [ -f 1.log.1 ] then gzip 1.log.1 fi #由於1.log.1已經被壓縮爲1.log.gz,所以可以直接將1.log改名爲1.log.1 mv 1.log 1.log.1 #從7到2,倒序循環 for i in `seq 7 -1 2` do #$i2比$i小1 i2=$[$i-1] #首先判斷1.log.7.gz是否存在,若存在則刪除 e_df 1.log.$i.gz #當1.log.6.gz存在,則把1.log.6.gz改名爲1.log.7.gz,依次類推 if [ -f 1.log.$i2.gz ] then mv 1.log.$i2.gz 1.log.$i.gz fi done ##說明:由於我們需要按照日誌大小切割,所以這個腳本寫完後,需要每分鐘執行一次。