写点小脚本:把重复三次以上的操作自动化
判断标准很简单:一件事做了三次以上,就该写成脚本。脚本不需要多复杂,能省下每次敲命令和回忆参数的时间就值。
一、脚本的第一行该写什么
#!/usr/bin/env bash
set -euo pipefail这四个选项能挡掉绝大多数低级 bug:
| 选项 | 作用 |
|---|---|
set -e | 任何命令失败就退出(不会带着错误继续跑) |
set -u | 引用未定义变量时报错(而不是当成空字符串) |
set -o pipefail | 管道中任一环失败就算失败 |
set -x | 打印每条执行的命令(调试时加) |
# 调试时这样跑,能看到每一步
bash -x script.sh二、范例一:一键部署脚本
#!/usr/bin/env bash
set -euo pipefail
HOST="baize"
REMOTE_DIR="/var/www/baize"
echo "==> 本地检查"
[ -f index.html ] || { echo "不在项目根目录"; exit 1; }
echo "==> 同步文件(先试跑)"
rsync -avz --delete --dry-run ./ "$HOST:$REMOTE_DIR/"
read -rp "确认部署?[y/N] " ok
[ "$ok" = "y" ] || { echo "已取消"; exit 0; }
rsync -avz --delete ./ "$HOST:$REMOTE_DIR/"
echo "==> 重载 Nginx"
ssh "$HOST" "sudo nginx -t && sudo systemctl reload nginx"
echo "==> 完成 $(date)"两个值得学的点:
read -rp让用户确认,避免手滑把生产环境覆盖;--dry-run先看会做什么,确认后再真跑。
三、范例二:每日巡检
#!/usr/bin/env bash
# check.sh —— 早晨花 10 秒看一遍机器
echo "== 负载 $(cat /proc/loadavg | cut -d" " -f1-3)"
echo "== 磁盘"
df -h / | tail -1
echo "== 内存"
free -h | head -2 | tail -1
echo "== 服务状态"
for s in nginx ssh fail2ban; do
if systemctl is-active --quiet "$s"; then
echo " ✓ $s"
else
echo " ✗ $s 未运行!"
fi
done
echo "== 证书剩余天数"
end=$(echo | openssl s_client -connect www.bzii.cn:443 -servername www.bzii.cn 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo " $(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 )) 天"四、范例三:批量处理文件
#!/usr/bin/env bash
set -euo pipefail
# 把目录下所有 .jpg 转 .webp
shopt -s nullglob
for f in *.jpg; do
out="${f%.jpg}.webp"
[ -f "$out" ] && { echo "跳过已存在: $out"; continue; }
cwebp -q 80 "$f" -o "$out"
echo "转换: $f -> $out"
done
# 批量重命名:把空格换成下划线
for f in *" "*; do
mv -- "$f" "${f// /_}"
doneshopt -s nullglob 很重要:没匹配到文件时不会把 *.jpg 当字面量处理。
五、范例四:写日志的小工具函数
#!/usr/bin/env bash
log() { echo "[$(date "+%F %T")] INFO $*"; }
warn() { echo "[$(date "+%F %T")] WARN $*" >&2; }
die() { echo "[$(date "+%F %T")] ERROR $*" >&2; exit 1; }
log "开始备份"
[ -d /var/www ] || die "目录不存在,退出"
warn "磁盘剩余不足 20%"统一带时间戳,日志才有排查价值。
六、参数处理
简单场景用位置参数就够了:
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "用法: $0 <start|stop|status> [服务名]"
exit 1
}
action="${1:-}"
svc="${2:-nginx}"
case "$action" in
start) sudo systemctl start "$svc" ;;
stop) sudo systemctl stop "$svc" ;;
status) systemctl status "$svc" ;;
*) usage ;;
esac七、常见错误
| 错误 | 正确做法 |
|---|---|
| 路径含空格没加引号 | 一律 "$var" |
[ $x = "y" ] 在 x 为空时崩 | 用 [[ ]] 或 "$x" 加引号 |
在脚本里用 cd 后相对路径乱了 | 开头记 ROOT=$(cd "$(dirname "$0")" && pwd) |
| 命令找不到 | cron 里 PATH 很短,用绝对路径 |
| Windows 编辑过 | sed -i "s/\r$//" script.sh 去掉 CRLF |
八、什么时候该换个语言写
脚本超过 100 行、开始出现复杂数据结构、需要处理 JSON/YAML 时,就该换成 Python。 Bash 的舒适区是:串联几条命令 + 简单判断 + 文件操作,超出这个范围别硬撑。
# 处理 JSON 用 jq,不要 sed 硬抠
curl -s https://api.example.com/data | jq -r ".items[] | select(.status=="ok") | .name"