野生Linux 实战脚本库第一弹:5 个让你效率翻倍的 Shell 脚本
这些脚本是我从日常运维里提炼出来的,每一个都实际跑过、改过、被凌晨三点的告警验证过。拿回去直接用,别客气。
事情是这样的。
前段时间整理自己的 ~/bin 目录,发现不知不觉攒了四十多个脚本。有些是一次性的临时救急,有些是用了两三年的老伙计。我把其中最常用的 5 个整理了出来——每一个都能独立跑,不需要装任何依赖,复制粘贴就能用。
脚本一:smart-backup — 带轮转的自动备份
场景:你有个目录需要每天备份,保留最近 7 天的,自动删旧的。
#!/bin/bash# smart-backup.sh — 备份目录并保留最近 N 天# 用法:./smart-backup.sh /path/to/source /path/to/backupsSOURCE="$1"BACKUP_DIR="$2"RETENTION_DAYS="${3:-7}" # 默认保留 7 天# 参数校验if [ -z "$SOURCE" ] || [ -z "$BACKUP_DIR" ]; then echo "用法: $0 <源目录> <备份目录> [保留天数]" exit 1fi# 生成带时间戳的备份文件名BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S).tar.gz"# date +%Y%m%d → 20260525,方便排序和人工阅读mkdir -p "$BACKUP_DIR"# -p → 目录不存在就创建,存在也不报错tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$SOURCE" .# -c → 创建归档# -z → gzip 压缩# -f → 指定输出文件# -C "$SOURCE" . → 先 cd 到源目录再打包(避免路径嵌套)# 删除超过保留天数的旧备份find "$BACKUP_DIR" -name "backup-*.tar.gz" -mtime +"$RETENTION_DAYS" -delete# -name → 只匹配备份文件,避免误删# -mtime +7 → 修改时间超过 7 天的# -delete → 直接删除匹配的文件echo "备份完成: $BACKUP_DIR/$BACKUP_NAME"echo "已清理 ${RETENTION_DAYS} 天前的旧备份"
⚠️ 注意:第一次用建议先把 -delete 去掉,用 -print 代替看看会删哪些文件,确认无误再加回去。
脚本二:log-clean — 安全清理日志文件
场景:日志目录快满了,但你不敢直接 rm -rf,怕删了正在写的文件搞出 df/du 不一致的幽灵问题。
#!/bin/bash# log-clean.sh — 安全清理过期日志# 策略:先 truncate(清空内容),再 rm(删除文件)LOG_DIR="${1:-/var/log}"DAYS_OLD="${2:-30}"MIN_FREE_MB="${3:-500}" # 剩余空间低于这个值才开始清# 先看磁盘还剩多少FREE_MB=$(df -m "$LOG_DIR" | awk 'NR==2 {print $4}')# df -m → 以 MB 为单位显示# awk NR==2 → 取第二行(数据行,第一行是表头)# $4 → 第四列 = 可用空间echo "当前剩余空间: ${FREE_MB}MB"if [ "$FREE_MB" -gt "$MIN_FREE_MB" ]; then echo "空间充裕,无需清理(阈值: ${MIN_FREE_MB}MB)" exit 0fiecho "开始清理 ${DAYS_OLD} 天前的日志..."# 第一步:truncate(把文件大小裁到 0,但不删 inode)find "$LOG_DIR" -name "*.log" -mtime +"$DAYS_OLD" -exec truncate -s 0 {} \;# truncate -s 0 → 把文件大小裁到 0 字节# 进程还能继续往这个文件写(fd 还开着),只是文件从 0 重新长了# 第二步:确认空间回来了再删除find "$LOG_DIR" -name "*.log" -mtime +"$DAYS_OLD" -delete# 这时候文件已经是 0 字节了,删不删对磁盘没影响但目录干净点# 处理压缩的老日志(直接删)find "$LOG_DIR" -name "*.gz" -mtime +"$DAYS_OLD" -deleteecho "清理完成。剩余空间: $(df -m "$LOG_DIR" | awk 'NR==2 {print $4}')MB"
💡 为什么先 truncate 再 rm?回看之前聊过的「文件删除不等于空间释放」——进程可能还抓着旧文件的 fd。先清空内容确保空间立即回收。
脚本三:health-check — 一分钟系统体检
场景:每天连上服务器的第一件事——快速扫一眼有没有异常。
#!/bin/bash# health-check.sh — 快速系统体检# 输出重点:磁盘、内存、负载、关键服务echo "========== 系统体检 $(date) =========="echo ""echo "【磁盘】"df -h / /data 2>/dev/null | grep -v "tmpfs\|snap"# 看根分区和数据分区,过滤掉临时文件系统echo ""echo "【内存】"free -h | awk 'NR==1 || NR==2'# free -h → 人性化显示内存# 只看表头 + Mem 行(不看 Swap 行)echo ""echo "【负载 + 进程数】"uptime# 输出样例: 10:23:45 up 30 days, 2 users, load average: 0.52, 0.38, 0.41# 1 分钟/5 分钟/15 分钟平均负载,三个数字都低于 CPU 核心数就正常echo "总进程数: $(ps aux | wc -l)"echo ""echo "【监听端口变化】"ss -tlnp | awk '{print $4}' | grep -v "Local" | sort# 只显示监听地址和端口,排序后方便对比# 建议先存一个 baseline,每次跟 baseline 对比看有没有新端口echo ""echo "【最近登录】"last -5# 最近 5 次登录记录# 看到不认识的 IP → 立刻警觉echo ""echo "========== 体检完成 =========="
脚本四:batch-ssh-copy — 批量 SSH 公钥分发
场景:新机器到手,要把你的公钥推到一堆服务器上,一个个 ssh-copy-id 太蠢了。
#!/bin/bash# batch-ssh-copy.sh — 批量分发 SSH 公钥# 先在一个文本文件里列出所有目标 IP,一行一个HOSTS_FILE="${1:-hosts.txt}"SSH_USER="${2:-root}"if [ ! -f "$HOSTS_FILE" ]; then echo "请先创建 $HOSTS_FILE,每行一个 IP 地址" exit 1fi# 先确保自己的公钥存在if [ ! -f ~/.ssh/id_rsa.pub ]; then echo "未找到 SSH 公钥,先生成:" ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa -N "" # -t rsa -b 4096 → RSA 密钥,4096 位 # -f → 密钥文件路径 # -N "" → 空密码(免密登录需要)fiwhile read -r HOST; do [ -z "$HOST" ] && continue # 跳过空行 [[ "$HOST" =~ ^# ]] && continue # 跳过注释行(# 开头) echo -n "分发到 $HOST ... " if ssh-copy-id -o ConnectTimeout=5 "${SSH_USER}@${HOST}" 2>/dev/null; then echo "成功" else echo "失败(IP 不通 / 密码不对 / 端口不是 22?)" fidone < "$HOSTS_FILE"# done < "$HOSTS_FILE" → 从文件逐行读取
脚本五:watchdog — 进程挂了自动拉起来
场景:某个服务偶尔自己死掉,但又没重要到要配 systemd 的 Restart=always。简单的 crontab 守护。
#!/bin/bash# watchdog.sh — 检查进程是否存活,挂了就拉起# 用法:./watchdog.sh nginx "systemctl start nginx"PROCESS_NAME="$1"RESTART_CMD="$2"if [ -z "$PROCESS_NAME" ] || [ -z "$RESTART_CMD" ]; then echo "用法: $0 <进程关键词> <重启命令>" echo "示例: $0 nginx 'systemctl start nginx'" exit 1fiif ! pgrep -f "$PROCESS_NAME" > /dev/null; then # pgrep -f → 按完整命令行搜索进程 # > /dev/null → 不输出匹配结果,只要知道有没有匹配 echo "[$(date)] $PROCESS_NAME 未运行,尝试拉起..." eval "$RESTART_CMD" sleep 2 if pgrep -f "$PROCESS_NAME" > /dev/null; then echo "[$(date)] 拉起来了" else echo "[$(date)] 拉取失败!请手动检查" fifi
# 在 crontab 里每分钟跑一次:# crontab -e* * * * * /usr/local/bin/watchdog.sh nginx "systemctl start nginx" >> /var/log/watchdog.log 2>&1# >> 追加日志,方便回来看一共重启了多少次
使用建议
- • 所有脚本放到
/usr/local/bin/ 下,chmod +x 之后就能直接敲名字运行 - • 每个脚本里标注了 ⚠️ 的地方,操作前先理解后果
- • 这些脚本都有意保持简单——不超过 40 行,打开就能看懂
下期预告:脚本库第二弹 —— 日志分析 + 数据管道。你在日常运维里最想自动化的是什么操作?评论区告诉我,我优先写。**
🐧 野生推荐:回复「野生」获取我的《Linux效率工具清单》,回复「手册」获取《Linux命令速查手册》