Shell編程 (Ⅱ)——函數、數組、告警系統

Shell編程 (Ⅱ)

十4、shell中的函數

shell函數,至關於一個個子shell,就是一個代碼段,定義完函數就能夠引用它php

格式:python

function f_name() {
                      command
             }
函數必需要放在最前面

function 後是函數的名字,而且 function 這個單詞是能夠省略掉的mysql

花括號{} 裏面爲具體的命令nginx

函數,能夠直接寫在腳本內,至關於直接調用sql

內建變量shell

  • $1 第一個參數
  • $2 第二個參數
  • $3 第三個參數
  • ...
  • ~
  • $n 第n個參數
  • $# 總共有幾個參數
  • $0 腳本名字
  • 案例一:打印參數案例
[root@ying01 shell]# vim fun01.sh

#!/bin/bash
input(){
     echo '$1'=$1 '$2'=$2 '$0'=$0 '$#'=$#
}
input 1 a b 9
[root@ying01 shell]# sh  fun01.sh 
$1=1 $2=a $0=fun01.sh $#=4
[root@ying01 shell]# sh -x fun01.sh 
+ input 1 a b 9
+ echo '$1=1' '$2=a' '$0=fun01.sh' '$#=4'
$1=1 $2=a $0=fun01.sh $#=4
  • 案例二:傳遞參數求和
  • 用於定義加法的函數,shell中定義的函數,必須放在上面
  • 在shell裏面須要優先定義函數,好比在調用這個函數的時候,函數尚未定義,就會報錯
  • 在想要調用哪個函數,就必須在調用語句以前,先定義這個函數
[root@ying01 shell]# vim fun02.sh 

#!/bin/bash

sum() {           //定義函數名稱
    s=$[$1+$2]
    echo SUM=$s
}
sum 1 2          //函數的參數

[root@ying01 shell]# sh -x fun02.sh 
+ sum 1 2
+ s=3
+ echo SUM=3
SUM=3
  • 案例三:輸入網卡的名字,檢查網卡的IP地址
[root@ying01 shell]# ifconfig |grep -A1 "ens33: "   //提出含有"ens33: " 當前行和下一行
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.112.136  netmask 255.255.255.0  broadcast 192.168.112.255
[root@ying01 shell]# ifconfig |grep -A1 "ens33: " |awk '/inet/ {print $2}'   
192.168.112.136    //刪選出含有inet行的,第二段內容,分段符默認爲空格

在函數中運用:編程

[root@ying01 shell]# vim fun03.sh 

#!/bin/bash
ip()                                                     //定義函數ip   
{
  ifconfig |grep -A1 "$eth " |awk '/inet/ {print $2}'
}

read -p "please input the eth name: " eth               //輸入網卡名字
UseIp=`ip $eth`                                         //定義函數參數,以及函數的值賦予UseIP
echo "$eth adress is $UseIp"

[root@ying01 shell]# sh -x fun03.sh 
+ read -p 'please input the eth name: ' eth
please input the eth name: ens33:                       //輸入網卡ens37:
++ ip ens33:
++ ifconfig
++ awk '/inet/ {print $2}'
++ grep -A1 'ens33: '
+ UseIp=192.168.112.136
+ echo 'ens33: adress is 192.168.112.136'
ens33: adress is 192.168.112.136

[root@ying01 shell]# sh -x fun03.sh 
+ read -p 'please input the eth name: ' eth
please input the eth name: ens37:                        //輸入網卡ens37:
++ ip ens37:
++ ifconfig
++ awk '/inet/ {print $2}'
++ grep -A1 'ens37: '
+ UseIp=192.168.24.130
+ echo 'ens37: adress is 192.168.24.130'
ens37: adress is 192.168.24.130

十5、shell中的數組

  • 定義數組,查看數組
[root@ying01 shell]# b=(1 2 3 4)      //定義數組b,1 2 3 4爲其元素
[root@ying01 shell]# echo ${b[@]}     //方法一:查看數組元素
1 2 3 4
[root@ying01 shell]# echo ${b[*]}     //方法二:查看數組元素
1 2 3 4
[root@ying01 shell]# echo ${b[1]}     //查看數組元素b1,即第二位
2
[root@ying01 shell]# echo ${b[4]}     //查看數組元素b4,爲空,由於只有4位,b4即爲第五位

[root@ying01 shell]# echo ${b[0]}     //b[0]=1 爲第一位
1
[root@ying01 shell]# echo ${#b[@]}    //查看數組元素總數 加#
4
[root@ying01 shell]# echo ${#b[*]}
4
  • 數組的元素的賦值
[root@ying01 shell]# echo ${b[*]}
1 2 3 4
[root@ying01 shell]# b[3]=qq       //把qq賦給b3,即第4位
[root@ying01 shell]# echo ${b[*]}
1 2 3 qq
[root@ying01 shell]# b[3]=100      //把100賦給b3,即第4位
[root@ying01 shell]# echo ${b[*]}
1 2 3 100
[root@ying01 shell]# b[5]=100      //把100賦給b5,由於b5不存在,則自動建立;注意b4不存在
[root@ying01 shell]# echo ${b[*]}
1 2 3 100 100
[root@ying01 shell]# b[4]=dd       //把dd賦給b4,即第5位;注意b5的區別
[root@ying01 shell]# echo ${b[*]}
1 2 3 100 dd 100                   //第5位爲dd,證實b4以前爲空
  • 數組元素的刪除
[root@ying01 shell]# echo ${b[*]}
1 2 3 100 dd 100
[root@ying01 shell]# unset b[5]         //刪除b5元素,即第6位
[root@ying01 shell]# echo ${b[*]}       //成功刪除100
1 2 3 100 dd
[root@ying01 shell]# unset b            //清空數組元素
[root@ying01 shell]# echo ${b[*]}
  • 數組中元素的截取
[root@ying01 shell]# a=(`seq 1 10`)       //定義數組a
[root@ying01 shell]# echo ${a[*]}
1 2 3 4 5 6 7 8 9 10
[root@ying01 shell]# echo ${a[*]:5:4}    //從第5位開,截取4位
6 7 8 9
[root@ying01 shell]# echo ${a[*]}
1 2 3 4 5 6 7 8 9 10
[root@ying01 shell]# echo ${a[*]:0-4:3}  //從倒數第5位(0 1 2 3 4)開始,截取3位
7 8 9
  • 數組中元素的替換
[root@ying01 shell]# echo ${a[*]}
1 2 3 4 5 6 7 8 9 10
[root@ying01 shell]# echo ${a[*]/2/11}     //把2替換爲11
1 11 3 4 5 6 7 8 9 10
  • 直接賦值(須要用括號括起來)
[root@ying01 shell]# echo ${a[*]}
1 2 3 4 12 6 7 8 9 10
[root@ying01 shell]# c=(${a[*]/6/18})  //把更新的數組 賦予c數組
[root@ying01 shell]# echo ${c[*]}      //打印c數組
1 2 3 4 12 18 7 8 9 10

十6、告警監控系統的構建

16.1 告警系統需求分析

  • 需求:使用shell定製各類個性化告警工具,但須要統一化管理、規範化管理。
  • 思路:指定一個腳本包,包含主程序、子程序、配置文件、郵件引擎、輸出日誌等。
  • 主程序:做爲整個腳本的入口,是整個系統的命脈。
  • 配置文件:是一個控制中心,用它來開關各個子程序,指定各個相關聯的日誌文件。
  • 子程序:這個纔是真正的監控腳本,用來監控各個指標。
  • 郵件引擎:是由一個python程序來實現,它能夠定義發郵件的服務器、發郵件人以及發件人密碼
  • 輸出日誌:整個監控系統要有日誌輸出。

要求 : 咱們的機器角色多種多樣,可是全部機器上都要部署一樣的監控系統,也就說全部機器無論什麼角色,整個程序框架都是一致的,不一樣的地方在於根據不一樣的角色,定製不一樣的配置文件。vim

程序架構:數組

各子目錄說明:bash

  • bin下是主程序
  • conf下是配置文件
  • shares下是各個監控腳本
  • mail下是郵件引擎
  • log下是日誌。

16.2 告警系統主腳本

通常腳本都放在/usr/local/sbin 目錄下,所以在此目錄下建立相應的目錄。

[root@ying01 ~]# cd /usr/local/sbin        
[root@ying01 sbin]# mkdir mon                        //在sbin下建立腳本主目錄
[root@ying01 sbin]# cd mon/
[root@ying01 mon]# mkdir mail bin shares conf log    //在主目錄下建立各子目錄
[root@ying01 mon]# ls
bin  conf  log  mail  shares

主腳本必須放在bin/下;

[root@ying01 mon]# cd bin/
[root@ying01 bin]# vim main.sh

 #!/bin/bash
# 是否發送郵件的開關,
export send=1
#只要把send 改爲了1 ,就會給下面全部的監控的項目都會發送郵件,export表示全部的變量會應用在全部的子腳本里(如果系統處於維護狀態,就須要關閉全部的服務,這時候>就須要先把告警關閉,不然會一直髮郵件)
# 過濾ip地址;能夠加定義一個hostname,這樣能夠知道是哪臺機器
export addr=`/sbin/ifconfig |grep -A1 "ens33: "|awk '/inet/ {print $2}'`
# 找一下當前腳本所在的目錄
dir=`pwd`
# 只須要最後一級目錄名
last_dir=`echo $dir|awk -F'/' '{print $NF}'`
# 下面的判斷目的是,保證執行腳本的時候,咱們在bin目錄裏,否則監控腳本、郵件和日誌頗有可能找不到
if [ $last_dir == "bin" ] || [ $last_dir == "bin/" ]; then
    conf_file="../conf/mon.conf"
else
    echo "you shoud cd bin dir"
    exit
fi
exec 1>>../log/mon.log 2>>../log/err.log
#日誌記錄
echo "`date +"%F %T"` load average"
#求出系統負載
/bin/bash ../shares/load.sh
#先檢查配置文件中是否須要監控502,到配置文件中遍歷一遍,看看是否須要監控502
if grep -q 'to_mon_502=1' $conf_file; then
    export log=`grep 'logfile=' $conf_file |awk -F '=' '{print $2}' |sed 's/ //g'`
#找出log的路徑
    /bin/bash  ../shares/502.sh
fi

16.3 告警系統配置文件

告警系統mon.conf內容,須要放在conf/目錄下;

主要定義一些開關,定義一些對應的日誌路徑,或者說監控mysql的用戶名和密碼,以及IP地址port端口等

[root@ying01 conf]# vim mon.conf

## to config the options if to monitor
## 定義mysql的服務器地址、端口以及user、password
to_mon_cdb=0
 ##cdb等於0 or 1, default 0,0 not monitor, 1 monitor
db_ip=10.20.3.13
db_port=3315
db_user=username
db_pass=passwd
## httpd   若是是1則監控,爲0不監控
to_mon_httpd=0
## php 若是是1則監控,爲0不監控
to_mon_php_socket=0
## http_code_502  須要定義訪問日誌的路徑
to_mon_502=1
logfile=/data/log/www.abc.com/access.log
## request_count   定義日誌路徑以及域名
to_mon_request_count=0
req_log=/data/log/www.abc.com/access.log
domainname=www.abc.com

在此定義log目的:

要考慮監控的機器確定不止1臺;要想要讓腳本通用,兼容性很強,就須要把全部須要監控的服務的日誌都載入到配置文件中,改動起來方便,免得後期改動起來一個一個的對應腳本去修改,提升生產效率。

16.4 告警系統監控項目

根據主腳本的定義:/bin/bash ../shares/load.sh 在shares目錄下建立load.sh腳本。

[root@ying01 shares]# vim load.sh

#! /bin/bash
load=`uptime |awk -F 'average:' '{print $2}'|cut -d',' -f1`
#計算系統負載
if [ $load -gt 10 ] && [ $send -eq "1" ]   //負載超過10,且不在維護模式
then
    echo "$addr `date +%T` load is $load" >../log/load.tmp  
#這條命令的目的是爲了發送日誌
    /bin/bash ../mail/mail.sh txwd188`.com "$addr\_load:$load" `cat ../log/load.tmp`
fi
echo "`date +%T` load is $load"

建立告警系統腳本 502.sh

[root@ying01 shares]# vim 502.sh

#! /bin/bash
d=`date -d "-1 min" +%H:%M`
c_502=`grep :$d: $log |grep ' 502 '|wc -l`
if [ $c_502 -gt 10 ] && [ $send == 1 ]; then
    echo "$addr $d 502 count is $c_502">../log/502.tmp
    /bin/bash ../mail/mail.sh $addr\_502 $c_502 ../log/502.tmp
fi
echo "`date +%T` 502 $c_502"

腳本釋義:

截取一分鐘前的日誌文件並判斷,若是502的次數超過10次,或者不在維護模式(主腳本已經定義)就調用mail發郵件,若是沒有,僅僅記錄日誌。

告警系統 disk.sh

[root@ying01 shares]# vim disk.sh

#! /bin/bash

LANG=en   //定義系統語言
rm -f ../log/disk.tmp
## 用空格或者%爲分隔符,篩選出來磁盤使用量的百分比。
for r in `df -h |awk -F '[ %]+' '{print $5}'|grep -v Use`  //查看各個磁盤的已用百分比,返回數字
do
    if [ $r -gt 90 ] && [ $send -eq "1" ]        //判斷百分比是否大於90,而且send=1
then
    echo "$addr `date +%T` disk useage is $r" >>../log/disk.tmp
    fi
done
if [ -f ../log/disk.tmp ]
then
    df -h >> ../log/disk.tmp
    /bin/bash ../mail/mail.sh $addr\_disk $r ../log/disk.tmp
    echo "`date +%T` disk useage is nook"
else
    echo "`date +%T` disk useage is ok"
fi

注意: [ ]+ 的用法;

[ #%]+ 這裏的意思 匹配空格、#、%任意一個或者多個;

十7、告警系統郵件引擎

mail.sh(告警收斂)

  • 作zabbix的時候,作過mail.py的腳本,在這裏,直接調用進行使用就能夠。
  • 可是告警郵件引擎核心,conf主配置文件調用到的都是mail.sh ,因此這裏須要定義調用mail.py的sh腳本
  • mail.sh目的是作告警收斂,若是不想作告警收斂,在發現問題的時候直接告警就好,可是,可能會發生1分鐘前發現問題,1分鐘戶問題解決,這樣就會變成誤報,這樣會很麻煩
  • 收斂的目的就是1分鐘前發現問題,而後到10分鐘後,服務尚未恢復,就會告訴管理人員10分鐘了服務還未恢復
[root@ying01 shares]# cd ../mail/
[root@ying01 mail]# ls /usr/lib/zabbix/alertscripts/mail.py 
/usr/lib/zabbix/alertscripts/mail.py
[root@ying01 mail]# cp /usr/lib/zabbix/alertscripts/mail.py .  //把mail.py複製到mail目錄下
[root@ying01 mail]# ls
mail.py
[root@ying01 mail]# vim mail.sh

log=$1          //log做爲一個變量,接收來自第一個參數的值
t_s=`date +%s`      //時間戳
t_s2=`date -d "2 hours ago" +%s`    //兩個小時前的時間戳
if [ ! -f /tmp/$log ]       //若是日誌文件不存在
then
    echo $t_s2 > /tmp/$log      //把兩個小時前的時間戳寫到日誌第一行,從下往上寫
    fi
t_s2=`tail -1 /tmp/$log|awk '{print $1}'`     //截取時間戳,最後一行
echo $t_s>>/tmp/$log        //追加當前時間戳,寫入
v=$[$t_s-$t_s2]     //時間戳的時間差(以秒爲單位)詳情以下①
echo $v
if [ $v -gt 3600 ]  //此處的if判斷是,當過了1小時若是仍是沒有恢復再次發告警郵件。調用mail.py(若是沒有恢復,每1小時發一次)
then
    ./mail.py  $1  $2  $3            //告警   
    echo "0" > /tmp/$log.txt        //生成一個新的.txt日誌。用來記錄告警
else
    if [ ! -f /tmp/$log.txt ]       //判斷有沒有這個日誌文件(計數器文件)
    then
    echo "0" > /tmp/$log.txt        //計數器歸零
    fi
    nu=`cat /tmp/$log.txt`          //查看計數器的數字
    nu2=$[$nu+1]                    //計數器+1
    echo $nu2>/tmp/$log.txt         //把計數器寫入日誌文件(重置)
    if [ $nu2 -gt 10 ]              
    then
        ./mail.py  $1 "trouble continue 10 min $2" "$3"         //表明着已經持續了10分鐘了
        echo "0" > /tmp/$log.txt    //從新歸零開始計數
    fi
fi

腳本解析:

  • 以秒爲單位的目的:腳本是每隔一分鐘執行一次的,若是觸發了就告警,若是短期搞定或者告警數量不少,告警信息就會影響你解決分析判斷問題,若是成千上百臺機器,那就是太多了
  • 腳本開始報警,log 的$1是什麼? 當報警的時候執行mail.sh 緊接着給了個參數,那個參數就是所謂的$1。t_s 時間戳 t_s2 2小時以前的時間戳,定義兩小時的緣由就是爲了執行下面的if語句,條件成立就實現了我們的報警條件,開始報警。
  • 發了郵件後,寫一個計數器在log.txt中,else咱們如今就不執行了,由於那是小於3600才執行的東西。
  • 腳本每分鐘執行一次,第二次開始,log,t_s,t_s2相對於第一次只是增長了一分鐘,判斷文件是否存在,由於上一次剛剛執行過,因此必定是存在的,t_s2就只是增長了60s 因此通過提取給變量v,判斷不超過3600,開始執行else的內容,第一次執行的時候已經建立了log.txt 因此是有的且nu爲0 nu2爲1,nu2不大於10,因此就是再次循環 。
  • 直到執行到最後一步nu2爲11了,那也就是到了10分鐘了,若是故障仍是存在,運維再次收到一個告警郵件,故障已經持續10分鐘了。這樣就實現了我們所說的「告警收斂」
  • 若是超過10分鐘再次發了郵件,而後執行計數器歸0,再次開始從新循環計數。
  • 最後告警解除了,再也不調用mail.sh,也就恢復了,不在執行mail.sh了。
  • 假如腳本在執行3分鐘的時候,忽然故障恢復了,腳本也就不會再次執行,而後計數器保持在2,在計數週期內若是恢復,不告警即不發郵件,但只有在一個小時之後故障纔會消失,大於3600的,若是在一個小時內再次報警,仍是按照以前的計數器繼續執行! 裏面用python寫的腳本 mail.py 在zabbix這課裏面;

十8、運行告警系統

腳本加入crontab 每分鐘執行一次

[root@ying01 mail]# crontab -e

0 0 * * * /usr/local/sbin/nginx_log_rotate.sh

進入bin目錄下,執行主腳本;

[root@ying01 mail]# cd ../bin/
[root@ying01 bin]# sh -x main.sh
+ export send=1
+ send=1
++ /sbin/ifconfig
++ grep -A1 'ens33: '
++ awk '/inet/ {print $2}'
+ export addr=192.168.112.136
+ addr=192.168.112.136
++ pwd
+ dir=/usr/local/sbin/mon/bin
++ awk -F/ '{print $NF}'
++ echo /usr/local/sbin/mon/bin
+ last_dir=bin
+ '[' bin == bin ']'
+ conf_file=../conf/mon.conf
+ exec

有問題能夠根據錯誤日誌,進行校驗;

[root@ying01 bin]# cat ../log/err.log
相關文章
相關標籤/搜索