2. Shell脚本基础:变量、条件判断、循环、函数、常用命令组合

Shell脚本这东西,说白了就是你把一堆Linux命令写进一个文件里,让机器自动去执行。我刚开始做运维那会儿,每天手动敲命令敲到手抽筋,后来学会了写脚本,才真正体会到什么叫「事半功倍」。这一章咱们就把Shell脚本的核心知识点过一遍,都是我在项目中反复用到的东西。

2.1 变量:脚本里的「小盒子」

变量就像一个个小盒子,用来存数据。Shell里的变量用起来很随意,不需要提前声明类型。

#!/bin/bash
# 定义一个变量,等号两边不能有空格
name="张三"
age=28
echo "我的名字是 $name,今年 $age 岁"

嗯,这里要注意:变量名和等号之间不能有空格。我第一次写的时候习惯性加了空格,结果脚本直接报错,找了半天才发现问题。

变量使用规则:

  • 引用变量用 $变量名${变量名}
  • 双引号里可以解析变量,单引号不行
  • 变量名区分大小写,$Name$name 是两个东西

我在项目中遇到过一个问题:有个同事用单引号包住了变量,结果打印出来全是 $name 这种字符串,而不是实际的值。记住,单引号是纯文本,双引号才会解析变量。

2.2 条件判断:让脚本学会「思考」

条件判断就是让脚本根据不同情况执行不同操作。Shell里用 ifelifelse 这套组合拳。

#!/bin/bash
score=85

if [ $score -ge 90 ]; then
    echo "优秀"
elif [ $score -ge 80 ]; then
    echo "良好"
else
    echo "继续努力"
fi

你想想看,为什么这里用 -ge 而不是 >=?因为Shell里的比较运算符有自己的写法:

运算符 含义 示例
-eq 等于 [ $a -eq $b ]
-ne 不等于 [ $a -ne $b ]
-gt 大于 [ $a -gt $b ]
-lt 小于 [ $a -lt $b ]
-ge 大于等于 [ $a -ge $b ]
-le 小于等于 [ $a -le $b ]

我曾经踩过的坑:判断文件是否存在时,[ -f "$file" ] 这个写法里,变量最好用双引号包起来。否则如果文件名为空,脚本会直接报语法错误。别问我怎么知道的……

2.3 循环:重复的事情交给机器

循环就是让脚本反复执行某段代码。Shell里最常用的是 for 循环和 while 循环。

for 循环:遍历列表

#!/bin/bash
# 遍历一组IP地址
for ip in 192.168.1.1 192.168.1.2 192.168.1.3; do
    echo "正在ping $ip ..."
    ping -c 1 $ip &>/dev/null && echo "$ip 通" || echo "$ip 不通"
done

我个人习惯用 for 循环来处理批量任务,比如批量检查服务器状态、批量修改配置。你想想看,如果100台服务器要挨个检查,手动操作得累死,但脚本几秒钟就跑完了。

while 循环:条件控制

#!/bin/bash
count=1
while [ $count -le 5 ]; do
    echo "第 $count 次执行"
    count=$((count + 1))
done

为什么有时候用 while 而不是 for?说白了,for 适合已知范围的遍历,while 适合不知道什么时候结束的场景,比如等待某个进程启动完成。

2.4 函数:把重复代码「打包」

函数就是把一段代码封装起来,起个名字,想用的时候直接调用。我建议你把经常用的功能写成函数,这样脚本会清爽很多。

#!/bin/bash

# 定义一个函数
check_port() {
    local host=$1
    local port=$2
    nc -zv $host $port &>/dev/null
    if [ $? -eq 0 ]; then
        echo "$host:$port 端口开放"
    else
        echo "$host:$port 端口未开放"
    fi
}

# 调用函数
check_port "192.168.1.1" 80
check_port "192.168.1.1" 443

小技巧:函数里的 local 关键字用来声明局部变量,避免变量污染。我在写复杂脚本时,每个函数都会用 local 声明变量,这样不同函数之间不会互相干扰。

我记得有一次写监控脚本,没注意变量作用域,结果一个函数里改了全局变量,导致另一个函数的行为完全乱套了。从那以后,我养成了在函数里用 local 的习惯。

2.5 常用命令组合:实战中的「组合拳」

单个命令功能有限,但组合起来威力巨大。我整理了几个我在项目中经常用的组合:

管道与重定向

# 查找日志中错误信息,统计出现次数
grep "ERROR" /var/log/app.log | sort | uniq -c | sort -rn

# 把进程信息保存到文件
ps aux | grep java > java_process.txt

命令替换

#!/bin/bash
# 获取当前日期,用于日志文件名
today=$(date +%Y%m%d)
echo "今天的日志文件:app_${today}.log"

# 获取服务器负载
load=$(uptime | awk '{print $NF}')
echo "当前系统负载:$load"

实战案例:批量备份配置文件

#!/bin/bash
# 批量备份 /etc 下的 .conf 文件
backup_dir="/backup/conf_$(date +%Y%m%d)"
mkdir -p $backup_dir

for conf_file in /etc/*.conf; do
    if [ -f "$conf_file" ]; then
        cp $conf_file $backup_dir/
        echo "已备份:$conf_file"
    fi
done

echo "备份完成,共备份 $(ls $backup_dir | wc -l) 个文件"

核心要点总结:

  • 变量用 $ 引用,双引号里能解析
  • 条件判断用 [],注意空格和运算符
  • 循环用 forwhile,根据场景选择
  • 函数用 function_name() {} 定义,局部变量用 local
  • 管道 | 和重定向 > 是组合命令的核心

说实话,Shell脚本这东西,光看是学不会的。我建议你打开终端,把上面的例子挨个敲一遍。遇到报错别慌,仔细看错误信息,大部分问题都是空格、引号、权限这些小细节。等你写了几十个脚本之后,这些东西就变成肌肉记忆了。