bash 抓捕异常_实现简单的监控脚本(Bash的执行和异常捕获)

当我们需要监控服务运行状态时,一般的策略是写定时脚本,定时执行探测服务状态,如果出现预期外情况,就报警。那么第一步我们就需要学会写一个监控脚本,这里我们会讲到bash的执行环境和异常捕获,以及一些简单的全局参数。

示例

先看一段shell代码,这个监控脚本会时刻监控我们的mysql进程是否正常服务,每2分钟执行一次:

#!/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等。

我们在使用bash执行脚本的时候,会创建一个新的Shell,这个Shell就是脚本的执行环境,并默认提供这个环境的各个参数。

异常捕获

set -e

set -o pipefail

set -u

set +e

我们的Shell会给脚本提供默认的环境参数,但是我们也可以用set命令来修改运行参数。在官方手册里一共有十几个参数,我们介绍常用的四个参数。

如果我们直接在终端运行set,不带任何参数,会显示所有的环境变量和Shell函数。

开启和关闭参数

我们常见的类似传参形式的set -e代表打开e代表的环境参数,相反的set +e代表关闭e代表的环境参数。

捕获单行异常

当我们遇到一个异常,如操作不存在的变量或者一行指令执行出错(行指令返回值不为0),Bash会默认输出错误信息,然后忽略这行错误,继续执行。这在大部分场景下并不是开发者想要的行为,也不利于脚本的安全和Debug。我们应该在错误出现的时候输出错误信息并中断执行。这样能够防止错误被累计和放大。

# 可执行文件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进程是否存在,输出不同的值,通过不同的脚本返回值来判断是否出现故障,并发送报警。当然更好的是,可以在挂掉时,尝试自动拉起进程。

参考资料

作者:赵帅强

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值