🤖 Shell 脚本
大家好,我是长安!Shell 脚本是 Linux 自动化的利器,学会它你就能让电脑帮你干活了。
🎯 本章目标
跟着长安读完这一章,你将学会:
- 编写简单的 Shell 脚本
- 使用变量和参数
- 条件判断和循环
- 常用的脚本技巧
🤔 什么是 Shell 脚本?
Shell 脚本就是把一堆命令写在一个文件里,让它们自动按顺序执行。
就像你给电脑写了一张"待办事项清单",它会一项一项帮你完成。
为什么要学 Shell 脚本?
- 🔄 自动化:重复的工作让电脑来做
- ⏰ 定时任务:每天自动备份、清理
- 🚀 批量操作:一键处理成百上千个文件
- 💼 工作必备:运维、开发都要用
📝 第一个脚本
创建脚本文件
# 创建脚本文件
vim hello.sh
编写脚本内容
#!/bin/bash
# 这是我的第一个 Shell 脚本
echo "Hello World!"
echo "今天是: $(date)"
echo "当前目录: $(pwd)"
运行脚本
# 方法一:添加执行权限后运行
chmod +x hello.sh
./hello.sh
# 方法二:用 bash 解释器运行
bash hello.sh
# 方法三:用 source 运行(会影响当前 shell 环境)
source hello.sh
💡 #!/bin/bash 是什么?
这行叫做 shebang(发音:sha-bang),告诉系统用什么程序来执行这个脚本。
常见的 shebang:
#!/bin/bash- 用 Bash#!/bin/sh- 用 sh#!/usr/bin/python3- 用 Python
📦 变量
定义变量
#!/bin/bash
# 定义变量(等号两边不能有空格!)
name="小明"
age=18
path="/home/xiaoming"
# 使用变量(加 $ 符号)
echo "我的名字是: $name"
echo "我今年 $age 岁"
echo "主目录: ${path}" # 用 {} 更清晰
⚠️ 常见错误
# ❌ 错误:等号两边有空格
name = "小明"
# ✅ 正确:等号两边不能有空格
name="小明"
特殊变量
#!/bin/bash
echo "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "所有参数: $@"
echo "参数个数: $#"
echo "上一个命令的退出码: $?"
echo "当前进程 PID: $$"
运行测试:
./script.sh arg1 arg2 arg3
用户输入
#!/bin/bash
echo "请输入你的名字:"
read name
echo "你好, $name!"
# 带提示的输入
read -p "请输入年龄: " age
echo "你今年 $age 岁"
# 静默输入(输入密码时)
read -s -p "请输入密码: " password
echo "" # 换行
echo "密码长度: ${#password}"
命令替换
#!/bin/bash
# 方法一:$() - 推荐
today=$(date +%Y-%m-%d)
files=$(ls -l | wc -l)
# 方法二:反引号 `` - 旧语法
today=`date +%Y-%m-%d`
echo "今天是: $today"
echo "当前目录有 $files 个文件"
🔀 条件判断
if 语句
#!/bin/bash
age=18
if [ $age -ge 18 ]; then
echo "你已成年"
else
echo "你未成年"
fi
if-elif-else
#!/bin/bash
score=85
if [ $score -ge 90 ]; then
echo "优秀"
elif [ $score -ge 80 ]; then
echo "良好"
elif [ $score -ge 60 ]; then
echo "及格"
else
echo "不及格"
fi
条件测试操作符
数字比较:
| 操作符 | 含义 | 示例 |
|---|---|---|
-eq | 等于 | [ $a -eq $b ] |
-ne | 不等于 | [ $a -ne $b ] |
-gt | 大于 | [ $a -gt $b ] |
-lt | 小于 | [ $a -lt $b ] |
-ge | 大于等于 | [ $a -ge $b ] |
-le | 小于等于 | [ $a -le $b ] |
字符串比较:
| 操作符 | 含义 | 示例 |
|---|---|---|
= | 相等 | [ "$a" = "$b" ] |
!= | 不相等 | [ "$a" != "$b" ] |
-z | 字符串为空 | [ -z "$a" ] |
-n | 字符串不为空 | [ -n "$a" ] |
文件测试:
| 操作符 | 含义 | 示例 |
|---|---|---|
-e | 文件存在 | [ -e file ] |
-f | 是普通文件 | [ -f file ] |
-d | 是目录 | [ -d dir ] |
-r | 可读 | [ -r file ] |
-w | 可写 | [ -w file ] |
-x | 可执行 | [ -x file ] |
逻辑运算
#!/bin/bash
# && 和
if [ $age -ge 18 ] && [ $age -le 60 ]; then
echo "劳动年龄"
fi
# || 或
if [ $status = "success" ] || [ $status = "done" ]; then
echo "完成"
fi
# ! 非
if [ ! -f file.txt ]; then
echo "文件不存在"
fi
💡 双中括号 [[ ]]
[[ ]] 是 Bash 的增强版条件测试,更安全、功能更多:
# 支持 && 和 || 在里面使用
if [[ $age -ge 18 && $age -le 60 ]]; then
echo "劳动年龄"
fi
# 支持正则匹配
if [[ $email =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]; then
echo "邮箱格式正确"
fi
🔄 循环
for 循环
#!/bin/bash
# 遍历列表
for fruit in apple banana orange; do
echo "水果: $fruit"
done
# 遍历数字范围
for i in {1..5}; do
echo "数字: $i"
done
# 遍历文件
for file in *.txt; do
echo "文件: $file"
done
# C 语言风格
for ((i=1; i<=5; i++)); do
echo "i = $i"
done
while 循环
#!/bin/bash
# 基本 while 循环
count=1
while [ $count -le 5 ]; do
echo "计数: $count"
((count++))
done
# 读取文件每一行
while read line; do
echo "行: $line"
done < file.txt
# 无限循环
while true; do
echo "运行中..."
sleep 1
done
until 循环
#!/bin/bash
# 直到条件为真才停止
count=1
until [ $count -gt 5 ]; do
echo "计数: $count"
((count++))
done
循环控制
#!/bin/bash
# break - 跳出循环
for i in {1..10}; do
if [ $i -eq 5 ]; then
break
fi
echo $i
done
# continue - 跳过本次迭代
for i in {1..5}; do
if [ $i -eq 3 ]; then
continue
fi
echo $i
done
🔧 函数
定义函数
#!/bin/bash
# 方式一
function greet() {
echo "Hello, $1!"
}
# 方式二(更兼容)
say_bye() {
echo "Goodbye, $1!"
}
# 调用函数
greet "小明"
say_bye "小红"
函数参数和返回值
#!/bin/bash
# 带参数的函数
add() {
local result=$(( $1 + $2 ))
echo $result # 用 echo 返回结果
}
# 获取返回值
sum=$(add 10 20)
echo "10 + 20 = $sum"
# 使用 return(只能返回 0-255 的数字)
is_even() {
if [ $(( $1 % 2 )) -eq 0 ]; then
return 0 # true
else
return 1 # false
fi
}
if is_even 4; then
echo "4 是偶数"
fi
📋 实用脚本示例
示例 1:备份脚本
#!/bin/bash
# 每日备份脚本
# 配置
SOURCE_DIR="/home/xiaoming/documents"
BACKUP_DIR="/home/xiaoming/backup"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_$DATE.tar.gz"
# 创建备份目录
mkdir -p $BACKUP_DIR
# 执行备份
echo "开始备份: $SOURCE_DIR"
tar -czvf "$BACKUP_DIR/$BACKUP_FILE" "$SOURCE_DIR"
if [ $? -eq 0 ]; then
echo "✅ 备份成功: $BACKUP_DIR/$BACKUP_FILE"
else
echo "❌ 备份失败"
exit 1
fi
# 删除 7 天前的备份
find $BACKUP_DIR -name "backup_*.tar.gz" -mtime +7 -delete
echo "已清理旧备份"
示例 2:系统监控脚本
#!/bin/bash
# 系统资源监控
echo "========== 系统监控报告 =========="
echo "时间: $(date)"
echo ""
# CPU 使用率
echo "【CPU 使用率】"
top -bn1 | grep "Cpu(s)" | awk '{print "使用率: " 100-$8 "%"}'
echo ""
# 内存使用情况
echo "【内存使用情况】"
free -h | awk 'NR==2{printf "已用: %s / 总计: %s (%.2f%%)\n", $3, $2, $3*100/$2}'
echo ""
# 磁盘使用情况
echo "【磁盘使用情况】"
df -h | awk '$NF=="/"{printf "根分区: %s / %s (%s)\n", $3, $2, $5}'
echo ""
# 负载
echo "【系统负载】"
uptime | awk -F'load average:' '{print "负载: " $2}'
echo ""
echo "========== 报告结束 =========="
示例 3:批量重命名
#!/bin/bash
# 批量重命名文件:给所有 jpg 文件添加前缀
PREFIX="photo_"
COUNT=1
for file in *.jpg; do
if [ -f "$file" ]; then
new_name="${PREFIX}$(printf "%03d" $COUNT).jpg"
mv "$file" "$new_name"
echo "重命名: $file -> $new_name"
((COUNT++))
fi
done
echo "完成!共处理 $((COUNT-1)) 个文件"
示例 4:服务检测脚本
#!/bin/bash
# 检测服务是否运行
check_service() {
local service=$1
if systemctl is-active --quiet $service; then
echo "✅ $service 正在运行"
return 0
else
echo "❌ $service 未运行"
return 1
fi
}
# 检测多个服务
services=("nginx" "mysql" "ssh")
for svc in "${services[@]}"; do
check_service $svc
done
🐛 调试脚本
调试技巧
# 方法 1:运行时调试
bash -x script.sh
# 方法 2:在脚本中开启调试
#!/bin/bash
set -x # 开启调试
# ... 代码 ...
set +x # 关闭调试
# 方法 3:遇错即停
set -e # 命令出错就停止执行
# 方法 4:使用未定义变量报错
set -u
常见错误排查
# 错误 1:命令找不到
# 检查 PATH 环境变量,或使用绝对路径
# 错误 2:权限不足
# chmod +x script.sh
# 错误 3:换行符问题(Windows 文件)
# dos2unix script.sh
# 错误 4:语法错误
# bash -n script.sh # 只检查语法不执行
📝 本章小结
脚本模板
#!/bin/bash
#
# 脚本名称: xxx.sh
# 描述: xxxx
# 作者: xxx
# 日期: xxxx-xx-xx
#
set -e # 遇错即停
# 变量定义
VAR1="value1"
# 函数定义
my_function() {
echo "doing something"
}
# 主逻辑
main() {
echo "开始执行..."
my_function
echo "执行完成"
}
# 执行
main "$@"
常用速查
| 功能 | 语法 |
|---|---|
| 变量 | name="value" |
| 使用变量 | $name 或 ${name} |
| 命令替换 | $(command) |
| if 判断 | if [ 条件 ]; then ... fi |
| for 循环 | for i in ...; do ... done |
| while 循环 | while [ 条件 ]; do ... done |
| 函数 | func() { ... } |
🚀 下一步
Shell 脚本学会了,接下来跟着长安了解 进程与服务 的管理!
—— 编程指南社区 · 长安
