先搞懂Bash自动化的核心场景
很多人学Shell脚本前会问:“我为什么要用它自动化?”其实答案就藏在你每天的重复工作里——比如:
1. 下载目录里堆了几百张图片,要按拍摄日期分类;
2. 服务器每天要备份数据库,得手动运行tar命令;
3. 测试环境部署应用,要重复执行“拉代码→编译→启动”;
4. 日志文件占了100G,得每周归档旧日志。

这些高频、重复、规则明确的任务,正是Bash自动化的主战场。Shell脚本的优势不是“高大上”,而是“用最少的代码解决最实际的问题”。
从0到1写第一个自动化脚本:文件批量整理
我们从最常见的“文件分类”需求入手,写一个能自动整理下载目录的脚本——把所有.jpg/.png图片按修改日期移动到对应文件夹。
需求拆解
- 遍历下载目录(比如
~/Downloads
)的图片文件; - 获取每个文件的最后修改日期(格式:2024-08);
- 按日期创建文件夹(比如
2024-08
); - 把图片移动到对应文件夹。
脚本代码(带详细注释)
#!/bin/bash
# 定义变量:源目录(下载目录)、目标目录(图片仓库)
SOURCE_DIR="$HOME/Downloads"
TARGET_DIR="$HOME/Pictures/ByDate"
# 确保目标目录存在(不存在则创建)
mkdir -p "$TARGET_DIR"
# 遍历源目录的图片文件(支持.jpg/.png)
for file in "$SOURCE_DIR"/*.{jpg,png}; do
# 跳过不存在的文件(比如目录里没有图片时)
[ -f "$file" ] || continue
# 获取文件的最后修改日期(%Y-%m表示年-月)
modify_date=$(stat -c %y "$file" | cut -d' ' -f1 | cut -d'-' -f1-2)
# 定义当前文件的目标文件夹
dest_folder="$TARGET_DIR/$modify_date"
# 创建目标文件夹(不存在则创建)
mkdir -p "$dest_folder"
# 移动文件(-v显示详细信息)
mv -v "$file" "$dest_folder"
done
echo "✅ 图片整理完成!共处理了$((i))个文件(i为遍历次数,可优化统计)"
运行脚本
- 保存为
sort_pictures.sh
; - 给脚本加执行权限:
chmod +x sort_pictures.sh
; - 运行:
./sort_pictures.sh
。
这个脚本的核心是变量引用(用$
调用)、条件判断(-f
检查是否为文件)和循环遍历(for
循环)——这些都是Bash自动化的基础语法。
自动化的关键:让脚本“聪明”起来
上面的脚本能工作,但还不够“智能”。真正好用的自动化脚本要解决三个问题:能接收参数、会处理错误、留运行痕迹。
1. 让脚本接收外部参数
比如我们想让用户自己指定源目录,可以用$1
(第一个参数)、$2
(第二个参数):
#!/bin/bash
# 接收用户输入的源目录(默认用~/Downloads)
SOURCE_DIR=${1:-$HOME/Downloads}
# 接收用户输入的目标目录(默认用~/Pictures/ByDate)
TARGET_DIR=${2:-$HOME/Pictures/ByDate}
# 后续逻辑和之前一致...
运行时可以这样用:./sort_pictures.sh ~/Desktop ~/Pictures/DesktopPhotos
,灵活性瞬间提升。
2. 让脚本“出错就停”
默认情况下,Bash脚本会忽略错误继续执行(比如mkdir
失败了,脚本还会继续移动文件)。用set -e
可以让脚本遇到错误立即退出:
#!/bin/bash
set -e # 开启“错误即退出”模式
SOURCE_DIR="$HOME/Downloads"
TARGET_DIR="$HOME/Pictures/ByDate"
mkdir -p "$TARGET_DIR" # 如果创建失败,脚本直接停在这里
# 后续逻辑...
3. 给脚本加“日志本”
没有日志的自动化等于“盲盒”——出问题了根本不知道哪里错了。我们可以用tee
命令把输出同时写到屏幕和日志文件:
#!/bin/bash
set -e
LOG_FILE="$HOME/.scripts/logs/sort_pictures.log"
# 确保日志目录存在
mkdir -p "$(dirname $LOG_FILE)"
# 写日志(-a表示追加,不覆盖旧日志)
echo "=====================================" | tee -a "$LOG_FILE"
echo "开始运行:$(date +'%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
# 核心逻辑...
echo "运行结束:$(date +'%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
这样打开~/.scripts/logs/sort_pictures.log
,就能看到每次运行的时间和结果。
让脚本“按时上班”:定时任务的正确打开方式
自动化的终极目标是“不用手动触发”——这就需要定时任务调度。Linux下最常用的工具是crontab
。
crontab的基础语法
crontab的格式是5个时间字段+命令,用空格分隔:
| 字段 | 含义 | 范围 |
|——–|———————|—————|
| 分钟 | 一小时中的第几分钟 | 0-59 |
| 小时 | 一天中的第几小时 | 0-23 |
| 日 | 一个月中的第几天 | 1-31 |
| 月 | 一年中的第几月 | 1-12 |
| 周 | 一周中的星期几 | 0-7(0=周日) |
比如:
– 0 2 * * *
:每天凌晨2点(分钟0,小时2,日/月/周任意);
– 30 18 * * 5
:每周五18点30分(分钟30,小时18,周5)。
给脚本加定时任务
以“每天凌晨2点备份数据库”为例:
1. 写一个备份脚本backup_db.sh
(假设用MySQL):
#!/bin/bash
set -e
LOG_FILE="/var/log/db_backup.log"
DB_NAME="mydb"
BACKUP_DIR="/backup/db"
MYSQL_USER="root"
MYSQL_PASS="your_password" # 注意:密码明文有风险,建议用.my.cnf配置
mkdir -p "$BACKUP_DIR"
echo "开始备份数据库:$(date)" | tee -a "$LOG_FILE"
# 用mysqldump备份数据库
mysqldump -u$MYSQL_USER -p$MYSQL_PASS $DB_NAME | gzip > "$BACKUP_DIR/$DB_NAME-$(date +%Y%m%d).sql.gz"
echo "备份完成:$(date)" | tee -a "$LOG_FILE"
2. 编辑crontab:crontab -e
;
3. 加入定时任务(注意用绝对路径):
0 2 * * * /home/user/.scripts/backup_db.sh # 每天凌晨2点运行
4. 保存退出,crontab会自动生效。
crontab的“坑”:环境变量
crontab运行脚本时,默认的环境变量(比如PATH
)很精简(只有/usr/bin:/bin
),如果脚本里用了mysql
这样的命令,可能会提示“command not found”。解决方法有两个:
– 在脚本里用绝对路径:比如/usr/bin/mysqldump
(用which mysqldump
查路径);
– 在crontab里加载环境变量:0 2 * * * source ~/.bashrc && /home/user/.scripts/backup_db.sh
。
避坑指南:那些让脚本“翻车”的细节
Shell脚本的“坑”大多藏在细节里,以下是高频踩坑点:
1. 空格不是“装饰品”
Bash对空格非常敏感,比如if
语句的[]
前后必须加空格:
# 错误(没有空格)
if[$USER == "root"]; then echo "root"; fi
# 正确(有空格)
if [ $USER == "root" ]; then echo "root"; fi
2. 路径要用“绝对路径”
比如脚本里用cd downloads
,如果从其他目录运行(比如/home
),会找不到downloads
文件夹。正确的做法是用绝对路径:
# 错误(相对路径)
cd downloads
mv *.jpg ../pictures
# 正确(绝对路径)
cd /home/user/downloads
mv *.jpg /home/user/pictures
或者用$(dirname $0)
获取脚本所在目录:
SCRIPT_DIR=$(dirname $(realpath $0)) # 脚本所在目录的绝对路径
cd "$SCRIPT_DIR/downloads"
3. 文件名有空格要“包起来”
如果文件名叫holiday photo.jpg
,直接用$file
会被拆成holiday
和photo.jpg
。解决方法是用双引号包裹变量:
# 错误(文件名有空格会报错)
mv $file $dest_folder
# 正确(双引号包裹)
mv "$file" "$dest_folder"
4. 权限要给“执行权”
脚本写好后,一定要用chmod +x script.sh
给执行权限,否则会提示“Permission denied”。
真实案例:用Bash自动化服务器备份流程
最后我们整合前面的知识,写一个服务器全量备份脚本——备份网站文件+数据库,并上传到远程存储(比如阿里云OSS)。
脚本代码
#!/bin/bash
set -e
# 配置变量
WEB_DIR="/var/www/html" # 网站根目录
DB_NAME="my_website" # 数据库名
DB_USER="root" # 数据库用户
DB_PASS="your_db_password" # 数据库密码
BACKUP_DIR="/var/backups" # 本地备份目录
OSS_BUCKET="oss://my-backup-bucket" # 远程OSS桶
LOG_FILE="/var/log/full_backup.log" # 日志文件
# 确保目录存在
mkdir -p "$BACKUP_DIR"
mkdir -p "$(dirname $LOG_FILE)"
# 写日志头
echo "=====================================" | tee -a "$LOG_FILE"
echo "全量备份开始:$(date +'%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
# 1. 备份网站文件(tar压缩,--exclude排除临时文件)
WEB_BACKUP="$BACKUP_DIR/web-$(date +%Y%m%d).tar.gz"
tar -czf "$WEB_BACKUP" --exclude="$WEB_DIR/tmp" --exclude="$WEB_DIR/cache" "$WEB_DIR"
echo "✅ 网站文件备份完成:$WEB_BACKUP" | tee -a "$LOG_FILE"
# 2. 备份数据库(mysqldump+gzip压缩)
DB_BACKUP="$BACKUP_DIR/db-$(date +%Y%m%d).sql.gz"
mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$DB_BACKUP"
echo "✅ 数据库备份完成:$DB_BACKUP" | tee -a "$LOG_FILE"
# 3. 上传到OSS(需要安装ossutil:https://help.aliyun.com/document_detail/120075.html)
ossutil cp "$WEB_BACKUP" "$OSS_BUCKET" >> "$LOG_FILE" 2>&1
ossutil cp "$DB_BACKUP" "$OSS_BUCKET" >> "$LOG_FILE" 2>&1
echo "✅ 上传OSS完成" | tee -a "$LOG_FILE"
# 4. 删除7天前的旧备份(避免占空间)
find "$BACKUP_DIR" -type f -mtime +7 -delete
echo "✅ 清理旧备份完成" | tee -a "$LOG_FILE"
# 写日志尾
echo "全量备份结束:$(date +'%Y-%m-%d %H:%M:%S')" | tee -a "$LOG_FILE"
运行效果
这个脚本会:
– 每周一凌晨1点运行(通过crontab设置);
– 备份网站文件和数据库;
– 上传到OSS做异地备份;
– 自动清理7天前的旧备份;
– 所有操作都有日志记录。
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/348