
简介面向Linux系统运维人员与自动化转型初学者的实用脚本合集整合系统运维中常见的一键化操作场景涵盖OpenSSH一键升级、Oracle数据库安装与备份、Zabbix监控部署、Docker环境搭建及Nginx升级等高频需求。压缩包共19个文件以Shell脚本为主9个sh辅以Markdown说明文档、RPM依赖包及源码压缩包整体大小约4.51MB结构清晰适合直接参考或按需修改后投入测试环境使用。通过配套的md教程和脚本注释可快速理解各自动化任务的关键步骤与排错思路降低手工重复操作带来的失误风险。目前已有2485人学习下载对于需要提升运维效率、规范日常变更操作的工程师具有较高的参考价值。1. Linux 运维自动化脚本让巡检、备份、日志清理准点自己跑做 Linux 运维的人迟早都会被一件事逼疯重复。同样的巡检命令、同样的日志清理、同样的备份上传今天敲一遍明天再敲一遍过两周还要翻 history 找上次的命令。这份 Linux 运维自动化运维脚本.zip 我拆过之后最大的感受是它没有玄学就是把运维日常里最高频的几类活儿——磁盘水位巡检、日志轮转压缩、目录备份、临时文件清理——全部做成了可以直接丢进 crontab 的 shell 脚本。适合两种人一种是刚从“手动敲命令”过渡到“想把操作收敛成自动化脚本”的运维新人另一种是手上管着十几台服务器、想统一操作方式和巡检口径的工程师。下面按我拆包和复现的顺序把脚本结构、关键参数、定时调度和坑位一次讲完。2. 拆开 zip 先看门道脚本分组、依赖清单和第一次运行顺序2.1 目录分组脚本、配置、日志各管各的这份 zip 解压后即使具体文件名有出入常见的组织思路也不会差太多。我一般会把这类脚本包整理成下面这个结构linux-auto-ops/ ├── bin/ # 所有可执行脚本 │ ├── sys_info.sh # 系统信息收集 │ ├── disk_check.sh # 磁盘水位巡检 │ ├── log_rotate.sh # 日志轮转与压缩 │ ├── backup.sh # 本地打包 远程同步 │ └── cleanup.sh # 过期临时文件清理 ├── etc/ │ └── env.conf # 公共配置路径、保留天数、远端地址 ├── log/ # 脚本运行日志 └── README.md # 使用说明和环境要求把脚本、配置、日志拆成三个目录是我特别看重的点。脚本里只写动作不写死具体路径路径和保留天数全放在etc/env.conf里每次运行结果统一追加到log/目录。这样换服务器部署时不用打开每个脚本改参数只需要改一个配置文件运维记录也能留痕。实际拆包时你可能会看到 bin 下多几个脚本比如 gz 压缩、网络连通性检测一类但职责划分逻辑是一致的。这份资源的核心解决路径就是一次手动操作沉淀成一个脚本一个脚本挂进定时任务彻底替代每天的重复劳动。zip 内的脚本按功能分成系统信息收集、日志管理、备份同步、清理回收四类实际使用中可以独立运行也可以由调度脚本串起来。下面我用一张表描述每个脚本的职责边界和预估运行耗时脚本文件主要职责典型耗时是否需要远端权限sys_info.sh收集主机名、内核、IP、负载、内存、磁盘使用率1 秒内否disk_check.sh检查分区水位超过阈值输出告警1 秒内否log_rotate.sh按天轮转日志、延迟压缩、清理过期份数视日志量而定需日志目录写权限backup.shtar 打包目录rsync 增量同步到备份机与数据量相关需要 SSH 密钥cleanup.sh清理 /tmp、缓存目录里的过期文件秒级视目标路径而定2.2 运行依赖先确认 bash、coreutils 和 rsync 在不在这类脚本包没有任何花哨的框架依赖标准环境就能跑但不代表不需要检查。我复现时踩过最轻的一跤就是脚本里用了stat -c %Y、xargs -r、hostname -I这三者在 CentOS 7 的默认环境没问题但在精简安装的容器镜像里可能缺hostname命令或 GNU findutils 版本过旧。所以上台之前先花十秒钟把这几个依赖确认清楚# 检查关键命令是否存在 for cmd in bash grep awk sed find tar gzip rsync hostname; do command -v $cmd /dev/null 21 || echo MISSING: $cmd done # 查看 bash 版本脚本里用了 Bash 4 的关联数组特性 bash --version | head -n 1依赖检查的逻辑很简单command -v比which更适合写在脚本里因为它不会受 PATH 和 alias 影响which在有些系统上还会输出额外提示。上面循环里没有任何输出说明这些基础命令都在。如果缺了rsync备份类脚本需要补装Debian/Ubuntu 用apt-get install -y rsyncCentOS/RHEL 用yum install -y rsync。还有个容易忽略的前提脚本文件必须有执行权限否则 crontab 里会报Permission denied。拿到 zip 解压之后建议统一处理一遍chmod x /opt/linux-auto-ops/bin/*.sh这一步不做后面所有定时任务都会在同一个地方翻车。2.3 第一次运行顺序语法检查、dry-run、单次实跑新拿到的脚本我的习惯是绝不直接丢进 crontab先走三步语法检查、空跑、单次实跑。因为脚本在自己机器上写得再顺到了生产环境都可能被环境差异打乱。先做语法检查# 语法检查只报错不执行 bash -n /opt/linux-auto-ops/bin/sys_info.sh echo syntax ok # 带跟踪模式执行逐行打印命令和结果 bash -x /opt/linux-auto-ops/bin/sys_info.sh 21 | head -n 50bash -n只做语法解析不真正执行适合在把脚本挂进 crontab 之前做静态检查。bash -x是调试利器每一行执行的命令都会被打印出来前缀是可以清楚看到变量有没有展开、命令有没有拼错、路径有没有写对。注意-x模式输出会很长日志量大的脚本建议先输出到临时文件再分段看不要直接糊一屏。这三步走完再手动执行一次完整脚本把输出内容和正常期望对比一下比如磁盘使用率是不是接近df -h的结果、生成的日志文件名日期对不对。确认输出合理之后脚本才算过了“单机可用”这一关。3. 核心脚本实战sys_info、log_rotate、backup 的参数与 crontab 调度3.1 sys_info.sh一条命令拿到整台服务器的体检报告系统巡检是最典型的重复劳动sys_info.sh的价值就是把十几条查询命令合并成一条。典型的实现长这样#!/usr/bin/env bash # sys_info.sh - 收集主机基础信息输出键值对便于直接写入监控 HOST$(hostname -s) KERNEL$(uname -r) IP$(hostname -I | awk {print $1}) LOAD$(uptime | awk -Faverage: {print $2}) MEM_TOTAL$(free -h | awk /^Mem:/{print $2}) MEM_USED$(free -h | awk /^Mem:/{print $3}) DISK_PERCENT$(df -h / | awk NR2{print $5}) echo host$HOST echo kernel$KERNEL echo ip$IP echo load$LOAD echo mem_total$MEM_TOTAL echo mem_used$MEM_USED echo disk_percent$DISK_PERCENT这段脚本的逻辑是用hostname -s取短主机名uname -r拿内核版本hostname -I配合awk {print $1}只取第一个 IPuptime用逗号分隔符摘出 load average 段free -h和df -h分别抽取内存总量/已用量和根分区使用率。输出设计成keyvalue格式是为了后续可以直接被监控采集器解析也可以被其他脚本source。这里有两个参数取舍值得说。第一free -h的单位是 GiB 还是 MiB 取决于版本和 CentOS 6 上老版本free的精度不同如果你要用字节数做数值比较建议去掉-h直接用free -m。第二df -h /里的NR2表示取第二行因为第一行是表头这个写法比tail -n 2 | head -n 1简洁但前提是你没有对df加-P之类的额外参数改变输出格式。3.2 log_rotate.sh日志轮转比你想的更容易踩时间边界日志轮转是运维里最容易“跑着跑着出问题”的一块。有些团队直接用系统自带 logrotate但遇到日志文件名要求带日期后缀、需要压缩前保留一定天数、又不想让 logrotate 的配置文件散落在各处时自己写一个脚本会更可控。常见实现如下#!/usr/bin/env bash # log_rotate.sh - 按天轮转日志14 天前的压缩30 天前的删除 LOG_HOME/var/log/myapp PREFIXapp today$(date %Y%m%d) cd $LOG_HOME || exit 1 # 把当前日志复制成带日期的存档然后原地清空 if [ -f ${PREFIX}.log ]; then cp ${PREFIX}.log ${PREFIX}.log.${today} : ${PREFIX}.log fi # 14 天前的轮转文件压缩成 .gz find $LOG_HOME -type f -name ${PREFIX}.log.* ! -name *.gz -mtime 14 -exec gzip {} \; # 30 天前的压缩包删除 find $LOG_HOME -type f -name ${PREFIX}.log.*.gz -mtime 30 -delete轮转的核心逻辑是三步先cp一份带日期的存档然后把原日志文件用: 原地清空而不是rm最后用find按时间做压缩和清理。cp而不是mv是考虑到正在写入的进程持有的是文件描述符mv之后进程还在往旧 inode 里写新的app.log反而收不到新日志cp加原地清空等于把当前内容“截断”之后让进程继续往同一个 inode 里写这是 Linux 上处理日志轮转的常见做法比mv安全。参数上PREFIX是日志文件名前缀today的格式决定了存档后缀-mtime 14表示 14 个 24 小时之前被修改的文件才压缩-mtime 30表示压缩包超过 30 天才删。这里最容易出问题的就是mtime边界后面第 4 章我会专门展开。如果你希望“压缩前的保留天数”和“总保留天数”可调建议把 14 和 30 提取成变量放进etc/env.conf。3.3 backup.shtar 打包配合 rsync做异地增量备份备份脚本的要点不是“能跑”而是“恢复的时候不出幺蛾子”。我见过太多备份脚本只做 tar不做异地同步磁盘一坏全没。所以这份资源里的 backup.sh 通常会用 tar 打本地包再用 rsync 推到远端备份机两者一结合既有完整的归档文件又省带宽#!/usr/bin/env bash # backup.sh - 本地打包 rsync 增量同步 SRC_DIR/data/www BACKUP_DIR/data/backup REMOTEbackup192.168.1.10:/data/backup/www STAMP$(date %Y%m%d_%H%M%S) NAMEwww_${STAMP}.tar.gz # 打包时不带绝对路径恢复时直接解压到指定目录 tar czf ${BACKUP_DIR}/${NAME} -C $(dirname $SRC_DIR) $(basename $SRC_DIR) # rsync 增量同步--delete 保持远端与本地一致 rsync -avz --delete ${BACKUP_DIR}/ ${REMOTE}/ # 本地只保留最近 5 份后面的全删 ls -1t ${BACKUP_DIR}/www_*.tar.gz | tail -n 6 | xargs -r rm -f这里最值得学习的参数是 tar 的-C配合dirname/basename先切到/data再打包www这样压缩包里第一层目录是www而不是data/www恢复的时候直接解到目标目录就行不用再手工调整一层路径。rsync -avz是归档模式加压缩传输--delete保证远端和本地目录完全一致本地删了的包远端也会删避免远端磁盘被历史备份撑爆。ls -1t | tail -n 6 | xargs -r rm -f是保留最新 5 份、删除更早备份的写法tail -n 6表示从第 6 行开始输出xargs -r在没有输入时不执行删除命令这是 GNU 版本特性BSD 系统上可能需要去掉-r用xargs踩到再看xargs: unterminated quote这类报错。3.4 挂进 crontab定时调度与输出重定向脚本单机跑通只是开始真正让它“自动化”的是 crontab。调度配置有几个强制性要求脚本必须用绝对路径、输出必须重定向、脚本内部最好自行处理 PATH。我的 crontab 配置长这样# crontab -e 添加以下内容 MAILTO 0 2 * * * /opt/linux-auto-ops/bin/backup.sh /opt/linux-auto-ops/log/cron.log 21 0 3 * * * /opt/linux-auto-ops/bin/log_rotate.sh /opt/linux-auto-ops/log/cron.log 21 */10 * * * * /opt/linux-auto-ops/bin/disk_check.sh /opt/linux-auto-ops/log/cron.log 21三个定时任务分别是每天凌晨 2 点备份、凌晨 3 点轮转日志、每 10 分钟巡检一次磁盘水位。MAILTO是关掉 cron 的默认邮件通知防止每次脚本输出都往系统邮箱里塞垃圾 log 21把标准输出和标准错误都追加到同一个日志文件这样排查问题时所有任务日志在一个文件里按时间翻就行。如果你不想让日志无限长大记得对log/目录也做一次轮转或者直接交给log_rotate.sh统一处理——把LOG_HOME指向这个目录即可。关于 PATHcrontab 环境里 PATH 往往只有/usr/bin:/bin而脚本里调用的rsync如果装在/usr/local/bin直接跑就是command not found。最省事的办法是在脚本开头统一export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin这样不管从什么环境执行都不会丢命令。4. 排错与避坑五条能把运维脚本干翻车的常见问题4.1 crontab 里脚本静默失败环境变量与 PATH 不继承现象脚本在终端手动执行一切正常但挂进 crontab 后完全没输出查看日志文件也是空的。原因cron 执行脚本时的环境是极简环境不加载/etc/profile、~/.bashrcPATH 只有系统默认值装在/usr/local/bin下的命令找不到。此外MAILTO默认会把输出寄给用户邮箱输出量小的时候日志文件里什么都看不到。解决脚本开头显式初始化 PATH 和 LANG关键脚本里对运行目录也做绝对定位export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin export LANGen_US.UTF-8 export LC_ALLen_US.UTF-8加上之后把 crontab 里的执行时间临时改成下一分钟等日志里出现输出再改回来。这是最快的验证方式。4.2 find -mtime 把要保留的备份删了现象backup.sh 里写find . -name www_*.tar.gz -mtime 7 -delete结果发现昨天的备份也被删了。原因-mtime 7的含义不是“超过 7 个自然日”而是“修改时间在 7×24 小时之前”。一个文件如果是周一早上 8 点生成的周三晚上执行脚本时它已经超过 48 小时但按自然日算它才出生第二天直接被7误删。解决理解 mtime 的粒度后把判断口径统一。按“自然日保留 N 份”的可靠做法是# 按文件名时间戳排序只保留最新 5 份其余删除 ls -1t ${BACKUP_DIR}/www_*.tar.gz | tail -n 6 | xargs -r rm -fls -1t按修改时间倒序排列tail -n 6跳过前 5 个xargs -r rm -f删除其余所有。这套逻辑完全不依赖 mtime 边界生产环境里我基本只用这一种方式来清理备份包。凡是涉及find -mtime的清理任务上线前先用find . -mtime 7 -printf %TY-%Tm-%Td %p\n打印出被命中文件的真实日期确认没有把最新的文件包含进去再执行。4.3 变量没加引号带空格的文件名被拆成两段现象cp $SRC_FILE $BACKUP_DIR/执行后源文件叫app log_2024.txt时目标目录里多了两个文件app和log_2024.txt或者直接报cp: cannot stat。原因shell 对未加引号的变量会做分词文件名里的空格被当成参数分隔符。这是 shell 脚本最经典的翻车点原因不复杂后果却在清理/备份场景容易放大——万一后面跟了rm连错删路径都找不到。解决凡用到变量承载文件路径一律加双引号如果路径里还可能有通配符和空格同时出现也先想清楚是否要用引号抑制展开cp $SRC_DIR $BK_DIR/ rm -f $BACKUP_DIR/$NAME整套脚本里搜索所有裸变量引用一条一条套上引号。Newer 在 Bash 里用set -u也能提前暴露未定义变量但引号是必须过的一关。4.4 日志删了磁盘不释放进程还占着已删除文件现象log_rotate.sh 执行完ls看日志文件已经删了但df -h检查磁盘空间一点没释放。原因服务进程打开了旧日志文件rm只是删除了目录项文件 inode 仍被进程引用数据块一直占着磁盘。这在 Java/Tomcat 和 Nginx 高并发日志场景尤其常见。解决找到仍然持有已删文件句柄的进程重启或重载服务让句柄释放# 列出所有“文件已被删除但仍在打开”的句柄 lsof L1 /var/log/myapp/*.log # 定位到具体 PID 后重启服务或让服务重开日志 systemctl restart myapp如果不想重启服务日志轮转一定要用“复制 截断”的方案也就是第 3.2 节里cp加: 的组合这样旧日志数据块不归进程持有rm之后磁盘才能真正回收。这里的教训是删日志不等于释放磁盘先用lsof L1看清谁占着再决定要怎么处理。4.5 数字前导 0 被解读成八进制08 和 09 直接报错现象脚本里写HOUR$(date %H)然后做算术比较[ $HOUR -lt 12 ]凌晨 8 点、9 点执行时报错08: value too great for base (error token is 08)。原因shell 的算术表达式把以0开头的数字当作八进制处理而08、09不是合法的八进制数字。date %H返回的小时恰好是带前导零的两位数字于是每天误差两小时。解决进制显式声明或者去掉前导零。推荐第一种因为改动最小HOUR$(date %H) HOUR$((10#$HOUR)) # 强制按十进制解析10#前缀在 Bash 算术里表示“按十进制读取后续字符串”08就变成 8。如果脚本里还有day$(date %d)、month$(date %m)参与算术运算同样要套10#。这类问题隐蔽在“看起来正常”的数字里排查时方向很容易跑偏。5. 再进一步给脚本加状态码、锁和告警一周内安全上线5.1 统一日志函数与退出码脚本凑合能跑和可以被信任之间差的是“错误可见性”。我一般会先给所有脚本套一层统一的日志函数和退出码规范#!/usr/bin/env bash # auto_job.sh - 统一日志 退出码示例 set -uo pipefail LOG_FILE${LOG_FILE:-/opt/linux-auto-ops/log/job.log} log() { echo $(date %F %T) [$1] $2 $LOG_FILE } # 业务逻辑失败时返回退出码 1日志记录 ERROR if cp /data/a /data/b; then log INFO copy ok else log ERROR copy failed exit 1 fi exit 0set -uo pipefail组合的含义是-u让未定义变量直接报错pipefail让管道命令的返回值取最后一个失败命令的状态而不是最后一条命令的状态。log函数把时间、级别、内容拼成一行追加到日志文件exit 1保证脚本出错时 crontab 能感知到。这样你在巡检时只要看日志里有几条ERROR而不是去猜脚本半夜到底跑没跑。5.2 用文件锁防止任务并发执行另一个隐蔽问题是执行时间超过调度周期。比如backup.sh在数据量大时跑了 15 分钟而 crontab 每 10 分钟触发一次就会两个备份进程同时跑磁盘 IO 飙升。用mkdir做一个原子锁是最简单的方案LOCK_DIR/tmp/$(basename $0).lock # mkdir 成功表示拿到锁失败说明已有实例在跑 if ! mkdir $LOCK_DIR 2/dev/null; then echo another instance running, skip this round exit 0 fi trap rmdir $LOCK_DIR EXITmkdir是原子操作两个进程同时抢锁时只有一个能成功。trap ... EXIT确保脚本退出时锁被清理即使中途报错退出也不会留下死锁。脚本执行完再看日志时“skip this round”这样的行就是锁在工作不是脚本漏跑。5.3 上线前的一周观察法我自己的习惯是任何新自动化脚本都强制走一周观察期阶段时间执行频率关注点单机验证第 1 天手动跑 2-3 次输出内容、退出码、日志文件大小灰度执行第 2-3 天每半天跑一次锁是否生效、备份大小是否稳定、时间是否超时准生产第 4-7 天按正式频率跑磁盘增长率、远端同步是否完整、有无ERROR日志观察期间不是只看“跑没跑”而是每次执行后记录三样东西退出码是不是 0、日志里有没有ERROR、生成的备份/清理结果和预期是否一致。连续七天没有异常再让它正式接管生产节奏。从那以后我每次把新一批脚本交给 crontab 之前都会强制走一遍 dry-run、锁、退出码三步宁可前面多花两天观察也不愿意半夜被磁盘写满的告警叫醒。这套习惯救过我很多次希望帮到你。本文还有配套的精品资源点击获取