文章目录
-
- 一、脚本基础结构
-
- [1. 解释器声明(Shebang)](#1. 解释器声明(Shebang))
- [2. 注释语法](#2. 注释语法)
- [3. 执行方式](#3. 执行方式)
- 二、变量与数据类型
-
- [1. 变量定义与使用](#1. 变量定义与使用)
- [2. 特殊变量](#2. 特殊变量)
- [3. 只读变量与删除变量](#3. 只读变量与删除变量)
- [4. 数值运算](#4. 数值运算)
- [5. 字符串操作](#5. 字符串操作)
- 三、输入与输出
-
- [1. 输出(echo)](#1. 输出(echo))
- [2. 输入(read)](#2. 输入(read))
- [3. 重定向](#3. 重定向)
- 四、条件判断(if语句)
- 五、循环语句
-
- [1. for循环](#1. for循环)
- [2. while循环](#2. while循环)
- [3. until循环(条件为假时执行)](#3. until循环(条件为假时执行))
- 六、case分支语句
- 七、函数
- 八、错误处理与调试
-
- [1. 错误中断](#1. 错误中断)
- [2. 调试模式](#2. 调试模式)
- [3. 检查命令执行结果](#3. 检查命令执行结果)
- 九、实战案例:日志清理脚本
- 十、新手避坑指南
- 总结
一、脚本基础结构
1. 解释器声明(Shebang)
脚本首行必须指定解释器,告诉系统用哪个程序执行:
bash
#!/bin/bash # 最常用,指定Bash解释器
#!/usr/bin/env bash # 跨平台兼容写法(推荐)
#!/bin/sh # POSIX标准Shell(部分系统sh是bash的简化版)
2. 注释语法
bash
# 单行注释
: <<'EOF'
多行注释
可以写很多行
EOF
3. 执行方式
bash
# 方式1:赋予执行权限后运行(推荐)
chmod +x script.sh
./script.sh
# 方式2:指定解释器运行(无需权限)
bash script.sh
sh script.sh
二、变量与数据类型
1. 变量定义与使用
bash
# 定义变量(等号两侧绝对不能有空格!)
name="Alice"
port=8080
path="/var/log"
# 使用变量
echo $name # 基本用法
echo ${name}_backup # 推荐:用{}明确变量边界,避免歧义
echo "$name" # 双引号内会解析变量
echo '$name' # 单引号内原样输出,不解析
2. 特殊变量
bash
$0 # 脚本文件名
$1 $2 # 第1、第2个位置参数
$# # 参数个数
$* # 所有参数(作为一个整体字符串)
$@ # 所有参数(每个参数独立,推荐用于循环)
$? # 上一条命令的返回码(0=成功,非0=失败)
$$ # 当前进程PID
$USER # 当前登录用户
$PWD # 当前工作目录
3. 只读变量与删除变量
bash
readonly PI=3.14 # 只读,不可修改
unset name # 删除变量
4. 数值运算
bash
a=10
b=3
# 方法1:$(()) 推荐
echo $((a + b)) # 13
echo $((a * b)) # 30
echo $((a % b)) # 1
# 方法2:expr(注意空格)
echo $(expr $a - $b) # 7
# 方法3:bc(支持浮点数)
echo "scale=2; 10 / 3" | bc # 3.33
5. 字符串操作
bash
str="Hello World"
echo ${#str} # 字符串长度:11
echo ${str:0:5} # 截取:Hello
echo ${str/World/Bash} # 替换:Hello Bash
三、输入与输出
1. 输出(echo)
bash
echo "普通输出"
echo -e "换行\n测试" # -e 支持转义符
echo "不换行" | tr -d '\n'
echo "错误信息" >&2 # 输出到标准错误流
2. 输入(read)
bash
read -p "请输入用户名:" username # 带提示
read -t 10 -p "请输入密码:" password # 10秒超时
read -s -p "密码:" pwd # -s 隐藏输入
3. 重定向
bash
echo "内容" > file.log # 覆盖写入
echo "追加" >> file.log # 追加写入
./script.sh > out.log 2>&1 # 标准输出+错误输出都写入文件
四、条件判断(if语句)
基本语法
bash
if [ 条件 ]; then
命令
elif [ 条件2 ]; then
命令
else
命令
fi
常用判断条件
bash
# 文件判断
[ -f "$file" ] # 文件存在且是普通文件
[ -d "$dir" ] # 目录存在
[ -e "$path" ] # 路径存在(不区分类型)
[ -r "$file" ] # 可读
[ -w "$file" ] # 可写
[ -x "$file" ] # 可执行
[ -s "$file" ] # 文件非空
# 字符串判断
[ -z "$str" ] # 字符串为空
[ -n "$str" ] # 字符串非空
[ "$a" = "$b" ] # 字符串相等
[ "$a" != "$b" ] # 字符串不等
# 数值比较
[ $a -eq $b ] # 等于
[ $a -ne $b ] # 不等于
[ $a -gt $b ] # 大于
[ $a -lt $b ] # 小于
[ $a -ge $b ] # 大于等于
[ $a -le $b ] # 小于等于
实战示例:检查Nginx服务
bash
if ps -ef | grep -v grep | grep -q nginx; then
echo "Nginx正在运行"
else
echo "Nginx未运行,正在重启..."
systemctl restart nginx
fi
五、循环语句
1. for循环
bash
# 遍历列表
for i in 1 2 3 4 5; do
echo "数字:$i"
done
# 范围遍历
for i in {1..10}; do
echo "第 $i 次"
done
# C语言风格
for ((i=0; i<10; i++)); do
echo $i
done
# 遍历文件
for file in /var/log/*.log; do
echo "处理文件:$file"
done
2. while循环
bash
count=0
while [ $count -lt 5 ]; do
echo "当前值:$count"
count=$((count + 1))
done
# 逐行读取文件
while IFS= read -r line; do
echo "行内容:$line"
done < file.txt
3. until循环(条件为假时执行)
bash
count=0
until [ $count -ge 5 ]; do
echo $count
count=$((count + 1))
done
六、case分支语句
适用于多值匹配,替代多个if-elif:
bash
case $1 in
start)
echo "启动服务..."
systemctl start nginx
;;
stop)
echo "停止服务..."
systemctl stop nginx
;;
restart)
echo "重启服务..."
systemctl restart nginx
;;
*)
echo "用法:$0 {start|stop|restart}"
exit 1
;;
esac
七、函数
bash
# 定义函数
check_service() {
local service_name=$1 # local 局部变量
if systemctl is-active --quiet "$service_name"; then
echo "$service_name 正在运行"
return 0
else
echo "$service_name 未运行"
return 1
fi
}
# 调用函数
check_service nginx
result=$? # 获取函数返回值
echo "返回码:$result"
八、错误处理与调试
1. 错误中断
bash
set -e # 任何命令失败立即退出脚本
set -u # 使用未定义变量时报错
set -o pipefail # 管道中任一命令失败则整体失败
2. 调试模式
bash
# 运行时调试
bash -x script.sh
# 脚本内开启/关闭调试
set -x # 开启:显示每条执行的命令
set +x # 关闭调试
3. 检查命令执行结果
bash
if ! command -v nginx &>/dev/null; then
echo "nginx未安装"
exit 1
fi
九、实战案例:日志清理脚本
bash
#!/usr/bin/env bash
set -euo pipefail
# 配置
LOG_DIR="/var/log/app"
KEEP_DAYS=30
BACKUP_DIR="/backup/logs"
# 函数:清理过期日志
clean_logs() {
local dir=$1
local days=$2
if [ ! -d "$dir" ]; then
echo "目录不存在:$dir"
return 1
fi
echo "清理 $dir 中超过 ${days} 天的日志..."
find "$dir" -name "*.log" -mtime +$days -delete
echo "清理完成"
}
# 主逻辑
echo "===== 日志清理开始:$(date) ====="
clean_logs "$LOG_DIR" "$KEEP_DAYS"
# 备份今日日志
mkdir -p "$BACKUP_DIR"
tar -czf "${BACKUP_DIR}/logs_$(date +%Y%m%d).tar.gz" "$LOG_DIR"/*.log 2>/dev/null || true
echo "===== 清理结束:$(date) ====="
十、新手避坑指南
| 常见错误 | 正确写法 | 说明 |
|---|---|---|
name = "value" |
name="value" |
等号两侧不能有空格 |
if [$a -eq $b] |
if [ $a -eq $b ] |
[ 和 ] 内侧必须有空格 |
echo $var_text |
echo ${var}_text |
用{}明确变量边界 |
| 忘记加执行权限 | chmod +x script.sh |
否则./script.sh报权限错误 |
| 不检查返回值 | `cmd | |
| 硬编码路径 | 使用变量 | 便于维护和跨环境部署 |
总结
Shell脚本的核心语法可以归纳为:变量 → 条件 → 循环 → 函数 → 错误处理。掌握这些基础模块,你就能构建出绝大多数自动化运维脚本。建议从简单的文件操作、服务检查脚本开始练习,逐步过渡到复杂的批量部署和监控告警场景。
💡 学习建议 :多用
bash -x调试,多写set -euo pipefail保底,养成检查$?的习惯,你的脚本会越来越健壮!