前言
会写 Bash 脚本,是运维和后端开发日常效率的分水岭。这篇把最核心的语法要素串一遍,看完能写出规范、能跑的脚本。
一、脚本骨架
从一个标准骨架开始:
#!/usr/bin/env bash
# 描述: 演示脚本骨架
set -euo pipefail # 出错即停 + 未定义变量报错 + 管道错误传递
LOG_FILE="/var/log/myapp.log"
log() {
echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"
}
main() {
log "脚本开始"
# ... 业务逻辑
log "脚本结束"
}
main "$@"
三件套说明:
set -e:任何命令返回非零就立即退出set -u:使用了未定义变量直接报错set -o pipefail:管道中任何一个命令失败,整条管道算失败
二、变量与引号
name="world" # 等号两边不能有空格!
echo "hello, $name" # 双引号: 会展开变量
echo 'hello, $name' # 单引号: 原样输出
echo "文件数: $(ls | wc -l)" # 命令替换
echo "当前目录: $(pwd)"
引号三原则:
- 变量引用一律加双引号:
"$name",防止空格和通配符展开把脚本搞崩 - 原样字符串用单引号
- 命令替换用
$()而不是反引号` `(可读性、可嵌套性更好)
特殊变量速查:
| 变量 | 含义 |
|---|---|
$0 |
脚本名 |
$1~$9 |
第 1~9 个参数 |
$# |
参数个数 |
$@ |
所有参数(每个独立) |
$? |
上一条命令的退出码 |
$$ |
当前脚本 PID |
三、算术运算
count=10
count=$((count + 1))
echo "$count" # 11
# 自增
((count++))
# 浮点运算 bash 原生不支持, 借助 bc
echo "scale=2; 10 / 3" | bc # 3.33
四、条件判断
if 语法(注意分号和空格)
if [ -f /etc/passwd ]; then
echo "文件存在"
elif [ -d /etc ]; then
echo "这是目录"
else
echo "都不满足"
fi
常用测试条件:
| 写法 | 含义 |
|---|---|
-f file |
是普通文件 |
-d dir |
是目录 |
-e path |
存在 |
-z "$s" |
字符串为空 |
-n "$s" |
字符串非空 |
"$a" = "$b" |
字符串相等 |
-eq / -ne / -gt / -lt |
数值 比较 |
-r / -w / -x |
可读/可写/可执行 |
数值比较示例:
load=$(uptime | awk -F'load average:' '{print int($2)}')
if [ "$load" -gt 4 ]; then
echo "负载偏高: $load"
fi
case 语句
case "$1" in
start) echo "starting...";;
stop) echo "stopping...";;
restart) echo "restarting...";;
*) echo "用法: $0 {start|stop|restart}"; exit 1;;
esac
五、循环
for 循环
# 遍历列表
for svc in nginx redis mysql; do
systemctl is-active "$svc" && echo "$svc ✓"
done
# 遍历数字范围
for i in {1..5}; do
echo "第 $i 次执行"
done
# 遍历命令输出(逐行)
for line in $(cat /etc/passwd); do
echo "$line"
done
# 更安全的逐行读取(保留空格)
while IFS= read -r line; do
echo "$line"
done < /etc/hosts
while + break/continue
retry=0
while true; do
if curl -sf http://localhost:8080/health >/dev/null; then
echo "服务健康"
break
fi
((retry++))
[ "$retry" -ge 10 ] && { echo "重试超限"; exit 1; }
sleep 3
done
六、实战小例子:批量检查主机存活
#!/usr/bin/env bash
set -euo pipefail
HOSTS=(192.168.1.10 192.168.1.11 192.168.1.12)
for ip in "${HOSTS[@]}"; do
if ping -c1 -W1 "$ip" &>/dev/null; then
echo "[UP] $ip"
else
echo "[DOWN] $ip"
fi
done
小结
- 骨架 +
set -euo pipefail是规范脚本的起点 - 变量永远记得
"$var"双引号 - 数值判断用
-eq/-gt,字符串判断用= - 复杂逐行处理用
while IFS= read -r
Bash 适合胶水和短脚本;逻辑超过两三百行时,就该考虑 Python 了。
本文是「Linux 基础」系列第 2 篇,下篇预告:Linux 文本处理三剑客 grep、sed、awk。