进阶实战:Shell 脚本入门
Shell 脚本(Scripting)是 Linux 的精髓。当你发现自己在重复敲同一组命令超过三次时,就应该考虑写一个脚本来自动化它。
1. 脚本的仪式感:Shebang 与权限
每一个 Shell 脚本的第一行必须指定解释器。
#!/bin/bash
# 这是一个注释
echo "Hello, Linux!" 如何执行?
- 赋予执行权:
chmod +x my_script.sh - 直接运行:
./my_script.sh - 或指定解释器运行:
bash my_script.sh
2. 基础交互与状态码
让脚本具备“对话”能力和“反馈”意识:
read: 获取用户输入。printf: 格式化输出(比echo更强大)。exit: 脚本退出的信号。0代表成功,非0代表出错。
printf "请输入你的名字: "
read USERNAME
echo "欢迎,${USERNAME}!"
# 如果没有输入则报错退出
if [ -z "$USERNAME" ]; then
echo "错误:名字不能为空!"
exit 1
fi3. 变量进阶:不仅是赋值
3.1 特殊变量列表
在脚本中,有一组“内置变量”可以让你轻松获取环境信息:
| 变量 | 含义 |
|---|---|
$0 | 脚本文件名本身 |
$1, $2... | 传入脚本的第 1, 2… 个参数 |
$# | 参数的总个数 |
$@ | 所有参数的列表 |
$? | 最重要:上一条命令的退出状态码(0 为成功) |
4. 逻辑控制:脚本的“大脑”
4.1 条件判断:[[ ]]
推荐使用双中括号 [[ ]],它比传统的 [ ] 更安全且支持正则。
# 检查文件是否存在且可写
if [[ -f "config.yaml" && -w "config.yaml" ]]; then
echo "配置文件就绪"
fi4.2 处理分支:case
当参数选项很多时,case 远比 if-else 清晰:
case "$1" in
start)
echo "启动服务..."
;;
stop)
echo "停止服务..."
;;
*)
echo "用法: $0 {start|stop}"
;;
esac4.3 循环:for 与 while
for: 遍历已知列表。while: 只要条件成立就一直跑(常用于读取文件行)。
5. 工程化实践:写出“稳健”的脚本
新手写脚本是为了“跑通”,老手写脚本是为了“不出错”。
5.1 开启安全模式
在脚本开头加上这行,可以让脚本在出错或变量未定义时立即停止,避免引发连锁灾难:
set -euo pipefail5.2 局部变量 local
在函数内部,务必使用 local 声明变量,防止污染全局环境。
5.3 静态分析工具:ShellCheck
强烈建议安装 ShellCheck。它能像语法检查器一样,指出你脚本中潜在的逻辑错误和不规范写法。
6. 自动化:定时任务与 Systemd
6.1 Cron:老牌定时任务
crontab -e 编辑任务列表:
0 2 * * * /path/to/script.sh(每天凌晨 2 点执行)
6.2 Systemd Timer:现代方案
相比 Cron,Systemd Timer 提供了更精确的日志记录、依赖管理和容错处理。它由 .service 和 .timer 两个文件组成。
7. 实战:系统体检与日志备份
#!/bin/bash
set -euo pipefail
# 配置
BACKUP_DIR="/tmp/system_logs"
LOG_FILE="/var/log/syslog"
DATE=$(date +%Y%m%d_%H%M%S)
# 确保目录存在
mkdir -p "$BACKUP_DIR"
echo "--- 系统体检开始 ---"
echo "磁盘占用:"
df -h | grep '^/dev/'
echo "备份日志中..."
tar -czf "${BACKUP_DIR}/log_${DATE}.tar.gz" "$LOG_FILE" 2>/dev/null
echo "完成!备份保存在:$BACKUP_DIR"将此脚本加入定时任务,或者将其封装为 Systemd 服务以实现开机自启。