##
# 本文为《shell脚本专家指南》一书的学习笔记。
##
7. 非直接变量
直接变量的概念
明确知道变量的名字,可以不先知道变量的值。
创建直接变量的三种方式。
直接赋值。 # a=b
存储一个命令的输出。 # a=`echo b`
存储某些类型的计算结果。 # a=$(( 1 + 2 ))
非直接变量的概念
在程序运行中创建的变量,可能不知道执行过程中需要的变量的具体名字。
也称动态变量。
通过eval命令,使用动态变量。
eval 命令,原文给的意思是"评估"。可以这么理解,正常来讲,我们向shell
提交一条命令,比如
var=abc; echo $var
那么输出是abc,shell在执行前,会将变量进行替换,就是将 $var
替换为了 abc ,实际shell执行的命令是 echo abc 。
eval的作用,是在将命令传入shell前,先进行了一次变量替换而不执行,
就是说命令的变量被替换了两次。那么看下面这条命令
var=abc; v=var; eval echo '$'$v
输出是 abc,可以理解为,eval echo '$'$v ,首先被eval命令进行变量替换,
就是 $v 替换为了 var, 那么命令再被传入shell执行前,shell看到
的命令就是 echo $var 了。
同样的,定义和提取变量,通过eval命令,就实现了非直接变量的使用。通过动态变量,监控日志文件。
示例-----------------------------------------------------------------
#!/bin/bash
# set -x
# 是否开启DEBUG,1开启,0关闭。
DEBUG=1
# 每次扫描日志文件的间隔,单位秒。
DELAY=60
# 示例内容可以排版为
# /var/log/messages:authentication%20failure:rebpeters:warn \
# /var/log/messages:recv%20failure::error
# 格式,日志文件名:扫描关键字:排除关键:日志级别
# 其中 %20 最后会用 空格 替换。
# 空格隔开的每个条目,都是需要监控的一个监控点。
# 可以实现对多个日志文件进行独立监控。
LOGCHKS="/var/log/messages:authentication%20failure:\
rebpeters:warn /var/log/messages:recv%20failure::error"
# 主循环,最后用sleep结尾,实现间隔扫描。
while :; do
# 用于每个主循环中,拼凑动态变量,实现次级循环的变量的隔离。
entry_count=0
# 次级循环,对LOGCHKS中的每个条目进行一次扫描。
for LOGCHK in `echo $LOGCHKS`; do
logfile=`echo $LOGCHK | cut -d: -f1`
strings=`echo $LOGCHK | cut -d: -f2`
strings=`echo $strings | sed -e "s/%20/ /g"`
exceptions=`echo $LOGCHK | cut -d: -f3`
exceptions=`echo $exceptions | sed -e "s/%20/ /g"`
notify=`echo $LOGCHK | cut -d: -f4`
entry_count=`expr $entry_count + 1`
suffix=`echo $logfile | sed -e s/\\\//_/g`
suffix=`echo $suffix | sed -e s/\\\./_/g`
# 判定是否为脚本首次执行,脚本首次执行时COUNT${suffix}_$entry_count变量为空。
# 需要对 BASE${suffix}_$entry_count 进行初始化,初始化为当前日志的行数。
if [ "`eval echo '$COUNT'${suffix}_$entry_count`" = "" ]; then
eval BASE${suffix}_$entry_count=`wc -l $logfile | awk '{print $1}'`
fi
eval COUNT${suffix}_$entry_count=`wc -l $logfile | awk '{print $1}'`
# 通过对比上次主循环结束时的行数 BASE${suffix}_$entry_count 和
# 当前日志行数 COUNT${suffix}_$entry_count 进行对比,判断日志是否追加或滚动。
#
# 日志追加,扫描出新增的行数,并用tail命令将新增日志进行处理。
if [ "`eval echo '$COUNT'${suffix}_$entry_count`" -gt "`eval echo '$BASE'${suffix}_$entry_count`" ]; then
LINES=`eval expr '$COUNT'${suffix}_$entry_count - '$BASE'${suffix}_$entry_count`
eval BASE${suffix}_$entry_count='$COUNT'${suffix}_$entry_count
if [ "$exceptions" != "" ]; then
MSGS=`tail -$LINES $logfile | egrep -i "$strings" | egrep -iv "$exceptions"`
test $DEBUG -gt 0 && echo "MSGS is $MSGS"
else
MSGS=`tail -$LINES $logfile | egrep -i "$strings"`
test $DEBUG -gt 0 && echo "MSGS is $MSGS"
fi
if [ "$MSGS" ]; then
if [ "$notify" != "error" ]; then
echo Send a warning notification.
else
echo Send a error notification.
fi
fi
# 日志发生滚动,直接对的当前日志进行全文件扫描。
elif [ `eval echo '$COUNT'${suffix}_$entry_count` -lt `eval echo '$BASE'${suffix}_$entry_count` ]; then
eval BASE${suffix}_$entry_count='$COUNT'${suffix}_$entry_count
if [ "$exceptions" != "" ]; then
MSGS=`cat $logfile | egrep -i "\"$strings\"" | egrep -iv "$exceptions"`
test $DEBUG -gt 0 && echo "MSGS is $MSGS"
else
MSGS=`cat $logfile | egrep -i "\"$strings\""`
test $DEBUG -gt 0 && echo "MSGS is $MSGS"
fi
if [ ! "$MSGS" ]; then
if [ "$notify" != "error" ]; then
echo Send a warning notification.
else
echo Send a error notification.
fi
fi
# 日志未变化,不做操作。
else
test $DEBUG -gt 0 && echo "No change in size of $logfile."
fi
done
sleep $DELAY
done