实现简单的监控脚本(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
相关文章
相关标签/搜索