2. Shell脚本基础回顾:变量、条件判断、循环

写巡检脚本,说白了就是跟文件、路径、日志打交道。Shell脚本就像你的"自动化双手",而变量、条件判断、循环就是这双手的核心关节。我个人习惯把这三样东西称为"脚本三要素"——你想想看,任何复杂的巡检任务,拆开来看无非就是:存数据(变量)、做判断(条件)、重复干(循环)。这一章,我把这三个基本功掰开揉碎讲清楚,顺便分享一些我当年踩过的坑。

Shell脚本基础三要素与巡检应用 变量 日志路径 · 时间阈值 · 状态 条件判断 文件检查 · 数值比较 · 模式匹配 循环 for遍历 · while读取 · until控制 审计日志巡检脚本 整合变量 · 条件判断 · 循环 → 自动化巡检 核心逻辑:数据存储 → 逻辑判断 → 重复执行

2.1 变量:脚本的"储物格"

变量就是给数据起个名字。在Shell里,变量不需要提前声明,直接用就行。比如你想存日志路径:

log_path="/var/log/audit"
retention_days=30

这里有个大坑——变量赋值等号两边不能有空格。我在项目中遇到过新人同事(包括当年的我)写成了 log_path = "/var/log/audit",结果脚本直接报错。为什么会这样?因为Shell把 log_path 当命令,= 和 "/var/log/audit" 当参数去了。记住:赋值就是 变量=值,中间一个空格都不能多。

引用变量时加 $ 符号:

echo "日志路径: $log_path"
echo "保留天数: ${retention_days}天"

我个人习惯给变量名加花括号,比如 ${log_path},这样在拼接字符串时更安全,也更容易看清楚变量边界。尤其是后面紧跟字母或数字时,花括号能避免歧义。

常用变量类型快速参考:

类型 示例 说明
普通变量 log_dir="/var/log" 存路径、文件名、时间等
环境变量 $HOME、$USER、$PATH 系统预置,可直接使用
特殊变量 $?、$0、$# 上条命令退出码、脚本名、参数个数
局部变量 local count=0 函数内使用,避免污染全局
变量命名建议:全小写 + 下划线分隔,比如 log_file_path、max_file_size。别用驼峰,也别用短横线(Shell会把短横线当成减号)。

2.2 条件判断:让脚本会"思考"

条件判断让脚本有了"脑子"。最基本的结构是 if-then-else,格式长这样:

if [ 条件 ]; then
    # 条件成立时执行
else
    # 条件不成立时执行
fi

条件判断的核心是 test 命令,[ ] 就是它的简写。我用一张表把这几种判断类型列清楚:

类型 表达式 说明
数值比较 [ $a -eq $b ] 等于
[ $a -ne $b ] 不等于
[ $a -gt $b ] 大于(还有 -lt -ge -le)
字符串比较 [ "$str" = "value" ] 等于(注意双引号不能省)
[ -z "$str" ] 字符串为空
文件判断 [ -f "$file" ] 文件存在且是普通文件
[ -d "$dir" ] 目录存在
[ -s "$file" ] 文件存在且非空

我最常用的组合:检查日志文件是否存在,且大小超过阈值。比如这样:

log_file="/var/log/audit/audit.log"
if [ -f "$log_file" ] && [ $(stat -c%s "$log_file") -gt 10485760 ]; then
    echo "日志文件超过10MB,需要处理"
fi
变量引用一定要加双引号!我曾经因为文件名包含空格(比如 audit 2024.log),没加引号导致判断失败,排查了大半天。加引号不是习惯问题,是保命操作。

如果条件分支超过两个,可以用 elif 串联:

if [ "$status" -eq 0 ]; then
    echo "正常"
elif [ "$status" -eq 1 ]; then
    echo "警告"
else
    echo "异常"
fi

2.3 循环:重复任务的"加速器"

巡检脚本里最常用的就是 for 循环和 while 循环。说白了,循环让你不用一遍遍地写重复代码。

for 循环适合遍历列表:

for host in server1 server2 server3; do
    echo "正在检查: $host"
    ssh "$host" "tail -100 /var/log/audit/audit.log"
done

while 循环适合逐行读取文件:

while read -r line; do
    echo "处理日志行: $line"
done < /var/log/audit/audit.log

再补充一个实用技巧:用 {..} 生成数字序列来循环:

for i in {1..5}; do
    echo "第${i}次巡检"
done
我建议:在循环体内加上 sleep 控制节奏,尤其当你要遍历大量文件或远程主机时。不加sleep,系统负载可能瞬间飙升,生产环境里这是大忌。

还有一种 until 循环,和 while 正好相反——条件为真时停止。用的场景不多,但某些等待场景下很顺手:

until [ -f "/tmp/flag" ]; do
    echo "等待标志文件生成..."
    sleep 5
done
echo "标志文件已就绪"

2.4 综合示例:巡检脚本片段

把变量、条件判断、循环组合起来,就是一个完整的巡检脚本雏形。下面这个例子,做了三件事:定义变量、遍历日志、检查大小:

#!/bin/bash
# 审计日志巡检脚本 - 基础版
log_dir="/var/log/audit"
threshold=1000000

echo "开始巡检: $(date)"

for log_file in "$log_dir"/*.log; do
    if [ -f "$log_file" ]; then
        file_size=$(stat -c%s "$log_file")
        if [ "$file_size" -gt "$threshold" ]; then
            echo "警告: $log_file 大小 ${file_size} 超过阈值"
        fi
    fi
done

echo "巡检结束: $(date)"
核心要点:这个脚本虽然简单,但包含了三要素的全部——变量存配置(log_dir、threshold)、条件判断做大小检查(-f、-gt)、循环遍历文件(for)。实际生产环境的巡检脚本,无非是在这个骨架上增加告警、记录、轮转等细节。记住这个骨架,后面的章节都是往上添砖加瓦。