實現簡單的監控腳本(Bash的執行和異常捕獲)

當咱們須要監控服務運行狀態時,通常的策略是寫定時腳本,定時執行探測服務狀態,若是出現預期外狀況,就報警。那麼第一步咱們就須要學會寫一個監控腳本,這裏咱們會講到bash的執行環境和異常捕獲,以及一些簡單的全局參數。html

示例

先看一段shell代碼,這個監控腳本會時刻監控咱們的mysql進程是否正常服務,每2分鐘執行一次:node

#!/bin/bash

#設置異常的捕獲和退出
set -e
set -o pipefail
set -u

#獲取當前腳本執行的命令和路徑
#self_name=`readlink -f $0`
#self_path=`dirname $self_name`

set +e

# 腳本主體
mysql_process_num=`ps aux | grep mysql | grep -v grep | grep -v bash | wc -l`

set -e

# 判斷腳本輸出,此處0爲異常
if [ "$mysql_process_num" -ge 1 ];
then
        echo "$mysql_process_num|proc_name=mysql"
else
        echo "0|proc_name=mysql"
fi

腳本命令解析

執行器

#!/bin/bash

首行表示此腳本使用/bin/sh來解釋執行,#!是特殊的標識符,後跟此腳本解釋器的路徑。
相似的還有/bin/sh, /bin/perl, /bin/awk等。mysql

咱們在使用bash執行腳本的時候,會建立一個新的Shell,這個Shell就是腳本的執行環境,並默認提供這個環境的各個參數。linux

異常捕獲

set -e
set -o pipefail
set -u
set +e

咱們的Shell會給腳本提供默認的環境參數,可是咱們也能夠用set命令來修改運行參數。在官方手冊裏一共有十幾個參數,咱們介紹經常使用的四個參數。sql

若是咱們直接在終端運行set,不帶任何參數,會顯示全部的環境變量和Shell函數。shell

開啓和關閉參數

咱們常見的相似傳參形式的set -e表明打開e表明的環境參數,相反的set +e表明關閉e表明的環境參數。安全

捕獲單行異常

當咱們遇到一個異常,如操做不存在的變量或者一行指令執行出錯(行指令返回值不爲0),Bash會默認輸出錯誤信息,而後忽略這行錯誤,繼續執行。這在大部分場景下並非開發者想要的行爲,也不利於腳本的安全和Debug。咱們應該在錯誤出現的時候輸出錯誤信息並中斷執行。這樣可以防止錯誤被累計和放大。bash

# 可執行文件run
#!/bin/bash
# 調用未定義的命令
foo
echo bar

# 執行該文件
$ ./run
./run: line 3: foo: command not found
bar

能夠看到輸出了錯誤信息,並繼續執行。併發

若是咱們想保證單行若是出現錯誤,就中斷執行腳本,能夠有三種寫法:函數

# 方法一
command || exit 1
# 方法二
if ! command; then exit 1; fi
# 方法三
command
if [ "$?" -ne 0 ]; then exit 1; fi

上面的方法統一爲判斷一行指令返回值是否爲0來判斷異常。
相似的,若是咱們的多個命令有依賴關係,即後者的執行須要前者成功,則須要寫:

command1 && command2

捕獲多行異常

上面的這種寫法過於複雜,若是咱們有一段腳本,則每行都須要單獨判斷,因此咱們須要使用全局的捕獲方式。

set -e會根據返回值來判斷命令是否失敗,只要腳本發生錯誤,就會終止繼續執行:

# 可執行文件run
#!/bin/bash
set -e
foo
echo bar

# 執行該文件
$ ./run
./run: line 3: foo: command not found

能夠看到腳本在發生錯誤後終止了執行。

若是咱們有一些代碼返回值爲0也不表明失敗,能夠先使用set +e關閉這個參數,稍後再打開。或者使用:

foo || true

捕獲管道命令異常

set -e不適合管道命令,所謂管道命令就是經過管道運算符|將不一樣功能的指令組合成一個複雜命令。好比:

# 查看全部進程,過濾包含mysql字段的進程,並對過濾後的進程數量計數
ps aux | grep mysql | wc -l

Bash會將最後一個子命令的返回值做爲整個命令的返回值。也就是若是中間的子命令出錯了,只要最後一個子命令返回值爲0,那麼異常便不會中斷整個腳本:

# 可執行文件run
#!/bin/bash
set -e
#set -o pipefail
foo | echo abc
echo bar

# 執行該文件
$ ./run
abc
./run: line 4: foo: command not found
bar

捕獲不存在的變量的異常

當咱們執行腳本時,遇到未定義的變量,Bash會默認忽略,並繼續執行。設置set -u參數,可以捕獲不存在的變量的錯誤:

# 可執行文件run
#!/bin/bash
set -e
set -u
echo $a
echo bar

# 執行該文件
$ ./run
./run: line 4: a: unbound variable

輸出內容的定位

若是咱們的腳本須要輸出不少東西,那麼你在終端只能看到連續輸出的內容,而沒法知道是哪一行指令輸出的結果。set -x參數可讓咱們先輸出執行的命令,再輸出結果。

# 可執行文件run
#!/bin/bash
set -x
echo `ps aux | grep mysql`
echo bar

# 執行該文件
$ ./run
++ ps aux
++ grep mysql
+ echo work 5191 0.0 0.0 106060 1464 '?' S May31 0:00 /bin/sh bin/mysqld_safe --defaults-file=my.cnf
work 5191 0.0 0.0 106060 1464 ? S May31 0:00 /bin/sh bin/mysqld_safe --defaults-file=my.cnf
+ echo bar
bar

簡寫的參數

set -e, set -u, set -o這些都是指令的簡稱,常規的寫法是set -o option-name,有時候咱們使用常規的寫法可讀性更高,有時候串起來使用更方便:set -eux

咱們能夠經過官方手冊-o參數看到全稱:

-e: -o errexit
-u: -o nounset
-x: -o xtrace

執行時設置環境參數

咱們也能夠在執行該腳本時手動指定:

bash -euxo pipefail run

獲取腳本和路徑

#獲取當前腳本執行的命令和路徑
#self_name=`readlink -f $0`
#self_path=`dirname $self_name`

首先須要瞭解到$0是腳本的執行文件路徑,相似的還有$?指最後的命令的返回值,$-set命令設置的全部Flag

# 可執行文件run
#!/bin/bash
echo $0

# 執行該文件
$ ../test/run
../test/run

readlink爲輸出符號連接的權威文件名,-f爲遞歸找到最終的文件名,如:

ln -s /home/work/run /home/work/run2
# 可執行文件run
#!/bin/bash
echo `readlink -f $0`

# 執行該文件
$ ./run2
/home/work/run

dirname輸出已經去除了尾部的"/"字符部分的名稱;若是名稱中不包含"/"
則顯示"."(表示當前目錄)。如:

dirname /usr/bin/sort 輸出"/usr/bin"。
dirname stdio.h               輸出"."。

腳本主體

後面的就是判斷mysql進程是否存在,輸出不一樣的值,經過不一樣的腳本返回值來判斷是否出現故障,併發送報警。固然更好的是,能夠在掛掉時,嘗試自動拉起進程。

參考資料

  1. Bash 腳本 set 命令教程:http://www.ruanyifeng.com/blo...
  2. 官方手冊:https://www.gnu.org/software/...
  3. linux中shell變量$#,$@,$0,$1,$2的含義解釋:https://www.cnblogs.com/fhefh...
  4. linux manpage: command --help
相關文章
相關標籤/搜索