简单进程监控程序
题目描述
作业题目:写一个 Shell
脚本来进行进程的死活监控
作业描述:此 Shell
脚本能检查配置文件中记录的进程是否在系统中运行。如果在系统中运行则显示进程ID(PID),如果不在运行则显示 DOWN 文字。
配置文件(check_procs.conf):
systemd
smbd
nmbd
#logd
sshd
脚本命名为check_proc.sh
, 执行脚本后的输出结果:
systemd: 1 5241 31842
smbd: DOWN
nmbd: DOWN
sshd: 5025 31840 31857
要求:
- 考虑各种错误场景的出错处理
- 关键代码要有合理注释
- 进行合理的函数功能封装
解题思路
题目的意图很明显,就是让我们读入配置文件,然后挨个判断文件中记录的进程是否在系统中运行,运行则给出 PID,否则显示 DOWN。根据输入和输出结果可以看出,注释掉的进程是不用判断的,直接略过。
核心的功能就是三步,读取文件、跳过注释、判断进程,最后将功能合理封装为函数运行即可。
详细编码实现
在编写代码之前,有一个非常重要的注意事项,Shell中的变量赋值是不能有空格的。比如我们要将给变量 a 赋值为5,应该写为 a=5
,而不能是 a = 5
。虽然加了空格会更美观,但是代码就不能按照预期运行了,所以赋值等号两边一定不要加空格!
1.判断进程
判断进程是程序最核心的功能,我们可以先进行进程判断,再将其他的功能加上。
使用非常典型的通过 grep
获取进程ID的方法,判断systemd
这个进程是否在运行。
program="systemd" # 进程名称
#获取进程PID,为空则进程未启动
pid=`ps -ef | grep $program | grep -v grep |awk '{print $2}'`
if [ "${pid}" == "" ]
then
echo "${program}: DOWN"
else
echo -ne "${program}: " # echo默认换行,-ne表示当前输出不换行
echo ${pid}
fi
- ps -ef ,显示所有的进程,e显示结果,f 显示完整格式
- ps -ef | grep xxx ,把包括 xxx 这个关键字的进程都显示出来
- grep -v grep,去除 grep 进程(即去除含有 xxx 关键字的这个grep查找进程本身)
- awk ‘{print $2}’,提取结果的第二列数据(因为第二列正是进程的PID)
2.检查文件
方便起见,我们就将文件放在 shell 脚本的同级目录下。新建配置文件 check_procs.conf
,将上面的配置文件示例全部写入
在脚本中编写判断文件是否存在的代码
fileName="check_procs.conf" # 文件名
BASE_PATH=$(cd `dirname $0`; pwd) # 获取当前目录
if [ ! -e "${BASE_PATH}/${fileName}" ] # 判断当前目录下文件是否存在
then
echo "文件不存在或无法读取"
exit 0
fi
- dirname $0,取得当前执行的脚本文件的父目录
- cd ‘dirname $0’, 进入这个目录(切换当前工作目录)
- pwd,显示当前工作目录(cd执行后的)
3.处理注释
读取文件,按每一行进行判断,line为变量,表示每一行的内容
判断当前行第一个字符是否为 #
,如果是则当前行全部是注释,忽略即可
cat $fileName | while read line # 读取文件,按行处理
do
if [ "${line:0:1}" == "#" ] # 第一个字符为 ‘#’
then
# 此处编写处理语句
fi
done
可以看到,当前这个注释判断十分简单,仅仅是将第一个字符为 # 的内容全部忽略
对于 ”注释在行内“ 、”多行注释“ 等这样的内容是不能判断的,还有待学习优化。
4.函数封装
Shell 中用户自定义的函数不需要像 C 一样指明类型,函数返回值可以用 return
来返回, return后跟一个数值,范围为 0-255。如果不用 return,将以最后一条命令运行结果作为返回值。
-
调用函数时可以向其传递参数,在函数内部使用
$n
的形式来获取参数的值。比如$1
表示第一个参数,$2
表示第二个参数,当n>=10时,需要使用${n}
来获取参数,比如第十个参数${10}
。 -
函数返回值在调用该函数后通过
$?
来获得。但是$?
仅对其上一条指令负责,一旦函数返回后其返回值没有立即保存入参数,那么其返回值将不再能通过$?
获得,所以函数返回值必须第一时间使用或保存。
有一点要注意的是,所有函数在使用前必须定义。所以说,应该将自定义函数放在脚本的开头部分,这样函数就可以正常使用了。
fileName="check_prog.conf"
program="" # program为变量,用来接收参数
# 读取文件
getFile(){
BASE_PATH=$(cd `dirname $0`; pwd)
if [ ! -e "${BASE_PATH}/${fileName}" ]
then
echo "文件不存在或无法读取"
exit 0
fi
}
# 跳过注释
crossNote(){
program=$1 # 接收传递的参数
if [ "${program:0:1}" == "#" ]
then
return 1 # 当前行为注释,返回1
fi
}
# 判断进程
judgeProgress(){
program=$1 # 接收传递的参数
pid=`ps -ef | grep $program | grep -v grep |awk '{print $2}'`
if [ "${pid}" == "" ]
then
echo "${program}: DOWN"
else
echo -ne "${program}: "
echo ${pid}
fi
}
编写主函数,首先读取文件,然后每行都直接去掉注释,再判断进程是否在运行。
# 主函数
main(){
getFile fileName # 判断文件存在
cat $fileName | while read line # 按行读取文件
do
crossNote $line # 每一行判断注释
if [ $? == 1 ] # 返回值为1,当前行为注释,忽略
then
continue
fi
judgeProgress $line # 判断进程是否运行
done
}
main
5.整体代码
#! /bin/bash
# 用于实现进程监控的shell脚本
fileName="check_procs.conf"
program=""
# 读取文件
getFile(){
BASE_PATH=$(cd `dirname $0`; pwd)
if [ ! -e "${BASE_PATH}/${fileName}" ]
then
echo "文件不存在或无法读取"
exit 0
fi
}
# 跳过注释
crossNote(){
program=$1
if [ "${program:0:1}" == "#" ]
then
return 1
fi
}
# 判断进程
judgeProgress(){
program=$1
#获得进程PID,为空则进程未启动
pid=`ps -ef | grep $program | grep -v grep |awk '{print $2}'`
if [ "${pid}" == "" ]
then
echo "${program}: DOWN"
else
echo -ne "${program}: "
echo ${pid}
fi
}
# 主函数
main(){
getFile fileName
cat $fileName | while read line
do
crossNote $line
if [ $? == 1 ]
then
continue
fi
judgeProgress $line
done
}
main
运行脚本
如果在运行脚本时遇到了错误,请参考这篇文章进行解决 Shell脚本使用常见问题说明
我们在当前目录下输入 ll
命令,确认配置文件已经存在
然后在终端中输入运行脚本的命令
./check_proc.sh
回车即可看到最终的运行结果