想象一下这样的场景:每天都要备份一批文件,每周都要清理一次日志,每次部署新版本都要执行一长串命令…这些重复性的工作如果每次都手动输入,不仅浪费时间,还容易出错。这时候,Shell 脚本就来拯救你了!
今天我们就来学习如何用 Shell 脚本把这些繁琐的工作自动化。相信我,学会了这个,你的效率能提升好几个档次。
Shebang 和基本结构
每个 Shell 脚本的第一行通常是 shebang,它告诉系统用哪个解释器来执行这个脚本。
1 2 3 4
| #!/bin/bash
echo "Hello, World!"
|
#!/bin/bash:指定使用 bash 解释器
#:注释符号,后面跟着的是注释内容
echo:输出命令,把后面的内容打印到终端
保存为 hello.sh 后,需要给它执行权限才能运行:
1 2
| chmod +x hello.sh ./hello.sh
|
变量和数据类型
Shell 中的变量定义很简单,不需要声明类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #!/bin/bash
name="星苒鸭" age=18
echo "我的名字是 $name" echo "今年 $age 岁"
greeting="你好,$name!" echo $greeting
readonly PI=3.14
unset name echo $name
|
注意:Shell 中的变量默认都是字符串类型,即使你写 age=18,它也还是字符串。做数学运算时需要特殊处理。
条件判断
条件判断让脚本可以根据不同情况执行不同的操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #!/bin/bash
age=20
if [ $age -ge 18 ]; then echo "你已经成年了" else echo "你还是个未成年" fi
score=85
if [ $score -ge 90 ]; then echo "优秀" elif [ $score -ge 80 ]; then echo "良好" elif [ $score -ge 60 ]; then echo "及格" else echo "不及格" fi
name="星苒"
if [ "$name" == "星苒" ]; then echo "欢迎回来,星苒!" fi
file="test.txt"
if [ -f "$file" ]; then echo "文件存在" elif [ -d "$file" ]; then echo "这是一个目录" else echo "文件或目录不存在" fi
|
常用的判断操作符:
- 数值比较:
-eq(等于)、-ne(不等于)、-gt(大于)、-lt(小于)、-ge(大于等于)、-le(小于等于)
- 字符串比较:
==(等于)、!=(不等于)、-z(空字符串)、-n(非空字符串)
- 文件判断:
-f(存在且是文件)、-d(存在且是目录)、-e(存在)、-r(可读)、-w(可写)
循环
循环让重复的工作变得简单。
for 循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #!/bin/bash
for fruit in 苹果 香蕉 橙子; do echo "我喜欢吃 $fruit" done
for i in {1..5}; do echo "数字: $i" done
for ((i=1; i<=5; i++)); do echo "第 $i 次循环" done
for file in *.txt; do echo "处理文件: $file" done
|
while 循环
1 2 3 4 5 6 7 8 9 10 11 12 13
| #!/bin/bash
count=1 while [ $count -le 5 ]; do echo "计数: $count" count=$((count + 1)) done
while IFS= read -r line; do echo "行内容: $line" done < file.txt
|
函数定义与调用
函数可以把一段代码封装起来,方便重复使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #!/bin/bash
greet() { echo "你好,$1!欢迎来到 Shell 脚本的世界。" }
greet "星苒" greet "张三"
add() { local result=$(($1 + $2)) echo $result }
sum=$(add 10 20) echo "10 + 20 = $sum"
show_info() { echo "姓名: $1" echo "年龄: $2" echo "城市: $3" }
show_info "李四" 25 "北京"
|
函数中的特殊变量:
$1, $2, $3...:位置参数,代表传递给函数的参数
$#:参数个数
$@:所有参数
$?:上一个命令的退出状态(0 表示成功)
local:声明局部变量
实战案例:自动备份脚本
让我们把学到的知识整合起来,写一个实用的自动备份脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #!/bin/bash
SOURCE_DIR=${1:-"/var/www/html"} BACKUP_DIR=${2:-"/backup"} DATE=$(date +"%Y%m%d_%H%M%S") BACKUP_NAME="backup_${DATE}.tar.gz" LOG_FILE="/var/log/backup.log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" }
if [ ! -d "$SOURCE_DIR" ]; then log "错误:源目录 $SOURCE_DIR 不存在!" exit 1 fi
if [ ! -d "$BACKUP_DIR" ]; then log "创建备份目录: $BACKUP_DIR" mkdir -p "$BACKUP_DIR" fi
log "开始备份 $SOURCE_DIR 到 $BACKUP_DIR/$BACKUP_NAME"
tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$SOURCE_DIR" . 2>/dev/null
if [ $? -eq 0 ]; then log "备份成功!文件大小: $(du -h "$BACKUP_DIR/$BACKUP_NAME" | cut -f1)"
log "清理超过 7 天的旧备份..." find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
log "备份任务完成" else log "备份失败!请检查日志" exit 1 fi
|
把这个脚本保存为 backup.sh,然后你可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| chmod +x backup.sh
./backup.sh
./backup.sh /home/user/projects /home/user/backups
crontab -e
|
总结
Shell 脚本是 Linux 运维的必备技能,它能让你:
- 自动化重复任务:把日常操作写成脚本,一键执行
- 批量处理文件:循环和条件判断让批量处理变得简单
- 系统集成:可以调用各种系统命令和工具
- 快速原型:对于简单任务,写个脚本比写 Python/Go 更快
当然,Shell 脚本也有它的局限性:复杂逻辑处理起来比较吃力,性能不如编译型语言。但对于大多数运维自动化任务来说,它已经是绰绰有余了。
接下来你可以:
- 多练习写各种实用的小脚本
- 学习更高级的 Shell 技巧(如信号处理、并发等)
- 探索
awk 和 sed 这两个强大的文本处理工具
- 了解
crontab 定时任务,实现真正的自动化
记住,最好的学习方式就是动手实践。找个实际的问题,试着用脚本解决它,你会发现 Shell 脚本真的很有趣!