
简介面向Linux运维工程师及shell初学者这份PDF系统整理了日常高频使用的自动化脚本覆盖日志错误过滤、服务连通性检查、旧文件清理、目录备份压缩、批量Ping多主机、SCP远程传输、用户home目录校验、日志实时监控等典型场景并扩展了自动化建用户、进程查找与kill、时区同步等进阶命令可直接参考或改造后用于生产环境。资源包体为1个PDF文档约100KB体积小巧便于离线查阅。目前已有1164人学习下载适合希望提升脚本效率、减少重复操作的运维人员。文档以实际笔记形式呈现通过grep -i ERROR、find -mtime 90、for循环、tail -fn0等具体示例讲解语法每段脚本附注释和判断结果既能帮助初学者理解shell执行逻辑也能让有经验者快速提取可用代码段实现日志监控、批量巡检和备份清理的自动化。1. 为什么 Linux 运维都该有一份属于自己的 shell 脚本库每天登录服务器查磁盘、翻日志、盯进程是 Linux 运维最日常的工作把这些操作固化成 shell 脚本才是从敲命令走向自动化运维的分水岭。以 PDF 形式流传的这类运维脚本合集整理的并不是高深框架而是把 Linux 常用命令、判断逻辑和文本处理组合成能直接跑、能交接、能复用的脚本集。新手能照着它完成 shell 脚本入门熟手则适合拿来做查漏补缺。Shell 脚本的价值不在语法多花哨而在下一次、下一个人、凌晨三点都能用同一套逻辑把问题处理掉。下面这套写法是我日常维护脚本库时一直在用的从脚本骨架讲起逐步落到巡检、日志、进程守护和高并发的批量任务最后给出上线前的自检手段。2. 给 shell 脚本打好地基执行环境、退出码与参数校验2.1 用 set -euo pipefail 让脚本在出错时立刻停下见过太多跑完了但每一步都在报错的脚本根源都是没设置执行环境。脚本头部建议固定这样写#!/usr/bin/env bash set -euo pipefail IFS$\n\t readonly SCRIPT_NAME$(basename $0) readonly RUN_TIME$(date %Y-%m-%d %H:%M:%S)set -e任意一条命令返回非零退出码就立即退出避免错误被后续命令掩盖。set -u引用未定义变量直接报错能提前暴露拼错的变量名。set -o pipefail管道中任何一段失败整条管道都算失败配合set -e才能拦住cmd1 | grep x里 grep 无匹配的情况。IFS$\n\t把 for 循环的默认分隔符从空格改成换行和 Tab处理带空格的文件名时不会莫名其妙裂成多段。set -e有两个例外要注意放在if条件、while条件或||右侧的命令失败不会触发退出这正好是判断类命令需要的语义。真正的坑是grep -q无匹配返回 1如果单独成行会被-e直接干掉。老手互查脚本第一眼基本都看头部这三行写错了后面再花哨也没用。2.2 统一的日志函数与输出规范一个脚本库里的输出格式如果不统一排障时一半时间在猜这条日志是哪个脚本打的。我一般在每个脚本里放一个 log 函数log() { local level$1; shift local ts ts$(date %Y-%m-%d %H:%M:%S) echo [$ts] [$level] $* ${LOG_FILE:-/tmp/ops.log} }调用级别约定如下表级别使用场景示例INFO正常流程节点log INFO disk check startedWARN超阈值但不阻断log WARN load 2x cores for 5minERROR命令失败、任务中断log ERROR log dir not existsFATAL参数非法、环境不满足log FATAL must run as root日志路径用${LOG_FILE:-/tmp/ops.log}这样的变量兜底生产环境把LOG_FILE指到/var/log/ops/下按脚本名分文件开发环境不动脚本就能切到/tmp。时间戳统一用%Y-%m-%d %H:%M:%S后面做文本分析时sort、awk直接按字符串排序即可省掉一次格式转换。2.3 getopts 与默认参数让脚本能被别人安全调用运维脚本一旦要在多台机器上复跑参数解析就不能靠$1硬编码位置。常见做法是getopts加默认值usage() { echo usage: $0 -d dir [-t threshold] 2 exit 2 } dir/var/log threshold85 while getopts d:t:h opt; do case $opt in d) dir$OPTARG ;; t) threshold$OPTARG ;; h) usage ;; *) usage ;; esac done shift $((OPTIND - 1)) [ -d $dir ] || usageshift $((OPTIND - 1))把选项消费掉让$1变成剩余的位置参数。这里新手常犯一个错选项字符串必须写成d:t:h冒号表示该选项需要参数漏了冒号-d /var/log里的路径会被当成普通参数后续所有判断全乱。参数合法性校验放在脚本开头宁可报错退出 2也不要带着脏参数往下跑。3. 服务器运维最高频的三种脚本巡检、日志清理与进程守护3.1 一键巡检 CPU、内存、磁盘与负载巡检脚本是脚本库里最常被翻出来改的因为阈值要跟着业务规模调。核心逻辑就四步采集指标、比对阈值、输出结果、异常时记录#!/usr/bin/env bash set -euo pipefail threshold_disk${1:-85} # 磁盘使用率阈值 load_cores$(nproc) disk_usage() { df -P / | awk NR2 {print $5} | tr -d % } load_avg$(cut -d -f1 /proc/loadavg) if [ $load_avg -gt $((load_cores * 2)) ]; then echo ALERT: loadavg ${load_avg} cores*2 fi usage$(disk_usage) if [ $usage -ge $threshold_disk ]; then echo ALERT: disk usage ${usage}% ${threshold_disk}% fidf -P的-P是 POSIX 输出格式避免行被折行导致awk NR2取错行负载直接读/proc/loadavg第一列比uptime再切分少一次文本处理。阈值经验值可以参考下表指标读取来源常用告警阈值备注CPUtop//proc/stat持续超过 90%按核数看更准内存free -mavailable 低于 20%别只看 used磁盘df -P使用率超过 85%根分区和业务分区分设负载/proc/loadavg超过核数 2 倍持续 5 分钟短时高负载勿误报巡检结果的文案要机器可读固定ALERT:前缀后面接告警函数或日志采集管道时用 grep 就能一次收敛不用在正则上再做文章。3.2 日志切割与过期清理别等磁盘写满再动手日志不清理巡检迟早报 ERROR。线上环境一般用 logrotate 管但脚本库里常见的场景是目标目录不固定、策略临时调整这种时候一个带保留天数的清理脚本更顺手#!/usr/bin/env bash set -euo pipefail log_dir${1:-/var/log/app} keep_days${2:-14} find $log_dir -type f -name *.log -mtime $keep_days -delete find $log_dir -type f -name *.log -size 200M \ -exec gzip -9 {} \;-mtime 14表示修改时间超过 14 天不能丢裸的 14 表示正好 14 天。执行顺序是先删过期、再压大文件保证磁盘紧张时不会因为先压缩而雪上加霜真正上线前把-delete换成-print先看一遍清单是标准的灰度手段。提示nginx 这类自己持有文件句柄的进程切割或压缩后必须kill -USR1 pid让它重新打开日志文件否则磁盘空间不会真正释放。这个细节在运维面试里也常被拿出来问。3.3 进程守护脚本与重启次数限制tomcat、jar、node 服务在 systemd 管不到的环境容器内、客户现场里需要一个挂了就拉起但不无限重启的守护脚本#!/usr/bin/env bash set -u proc_nameapp-server max_restarts3 restart_file/tmp/${proc_name}.restarts if ! pgrep -f $proc_name /dev/null; then count$(cat $restart_file 2/dev/null || echo 0) if [ $count -ge $max_restarts ]; then echo FATAL: exceed max restarts /var/log/ops/watchdog.log exit 1 fi nohup /opt/app/start.sh /var/log/ops/app.log 21 echo $((count 1)) $restart_file else rm -f $restart_file # 进程活着就清零计数 fipgrep -f匹配完整命令行它会连脚本自身的命令行一起匹配所以脚本文件名最好和业务进程名错开。重启计数写在/tmp下的文件里是为了脚本被 cron 重复拉起时状态仍能共享。这里用set -u而不用set -e是因为pgrep无匹配返回 1 是正常判断路径不能被-e掐断。nohup ... 启动后进程要确认不会收到 SIGHUP标准做法是把调度入口放到 cron 里让 init 进程接管子进程。4. 让批量脚本更可靠超时控制、并发执行与告警通知4.1 命令超时处理防脚本卡死在等待上批量跑脚本最怕某台机器网络不通一条ssh或curl卡住后面全部排队。GNU coreutils 自带的timeout是最直接的方案set e timeout 10 ssh -o ConnectTimeout3 userhost uptime rc$? set -e if [ $rc -eq 124 ]; then echo host timeout /tmp/ops_ssh_fail.log elif [ $rc -ne 0 ]; then echo ssh failed with ${rc} /tmp/ops_ssh_fail.log fitimeout的退出码 124 专门表示命令因超时被终止用它可以区分执行失败和卡死。为什么外层要包set e再set -e因为set -e存在时timeout返回非零会直接中断脚本rc$?根本执行不到。ssh自带的ConnectTimeout只控制 TCP 建连不控制连接后远端命令的执行时长所以两层超时必须都设curl同理--connect-timeout管建连、--max-time管总时长缺一不可。4.2 多主机并发巡检for 循环、wait 与 xargs -P 的取舍shell 脚本的 for 循环串行跑几十台机器不可接受常见改法是后台任务加waithosts(web-01 web-02 db-01) out_dir/tmp/ops_check mkdir -p $out_dir for host in ${hosts[]}; do ( ssh -o ConnectTimeout3 $host \ df -P / | awk NR2 {print \$5} $out_dir/$host.disk 21 echo $host done ) done waitwait是并发脚本的灵魂不加它脚本主体结束后台任务被 shell 带着退出结果文件可能还没写完。加wait的组合等于简易线程池但控制不了并发上限。要精确限流时换xargs -P方案并发控制结果收集适用规模forwait无限制按文件写十几台以内xargs -P 10精确控制按行输出几十到上百台parallel最灵活持久化、重试需要额外安装xargs -P的标准写法是seq 1 50 | xargs -P 10 -I{} sh -c check_one {}主机列表从管道喂入天然限制并发数。注意sh -c里引号层级很容易出错复杂逻辑建议先写成函数文件再用export -f导出。提示ssh 批量执行前先跑一轮ssh -o BatchModeyes userhost true确认免密BatchMode 会直接拒绝密码交互避免脚本在密码提示上集体卡死。4.3 告警函数把异常直接推到通知渠道巡检发现问题只写日志不够要能直接触达人员。不同团队的通知渠道不统一脚本库里通用做法是封装一个send_alertsend_alert() { local subject$1 body$2 local webhook${ALERT_WEBHOOK:-} [ -n $webhook ] || { echo no webhook, skip; return 0; } curl -fsS --connect-timeout 3 --max-time 10 \ -H Content-Type: application/json \ -d {\msgtype\:\text\,\text\:{\content\:\${subject} ${body}\}} \ $webhook || echo alert send failed /tmp/ops_alert.log }ALERT_WEBHOOK从环境变量取而不写死在脚本里脚本库才能跨环境复用提交到代码仓库时也不会泄露 token。curl失败不阻断主流程告警丢了要保证巡检本身不挂-f让 HTTP 4xx/5xx 按失败退出加上两个超时参数防止 curl 也卡住。批量任务里更推荐攒批全部机器跑完按主机名收敛成一条消息发出避免告警轰炸。通知渠道可以是钉钉机器人、企业微信机器人或邮件的 SMTP 封装函数接口保持一致换渠道只改函数体不动调用方。5. 脚本上线前自检与成果沉淀从可用到可交接5.1 shellcheck、语法检查与灰度验证脚本写完别直接上生产先过三道检查bash -n check_disk.sh # 1. 只查语法不执行 shellcheck -x check_disk.sh # 2. 静态检查标出未引用变量、路径问题 bash -x check_disk.sh -d /tmp 21 | head -50 # 3. 跟踪每行展开后的真实命令shellcheck会标出 SC2086变量未加引号、cd后不校验目录是否存在等典型问题比人眼靠谱得多。灰度验证要养成一个习惯给变更类脚本加--dry-run开关删除、压缩、重启都只打印将要执行的命令而不真执行灰度机跑通后再开全量。5.2 把脚本库整理成手册注释规范与 PDF 输出标题里的 PDF 提醒了一件事脚本库必须能交接。我的做法是给每个脚本头部写固定注释块包含用途、依赖命令、参数表、退出码和最近变更再用 awk 把所有脚本的头部注释抽出来合成手册for f in /opt/ops/scripts/*.sh; do echo ## $f awk /^#!/{next} /^#/{gsub(/^# ?/,); print; next} {exit} $f done /tmp/ops_manual.mdawk 的规则是跳过 shebang 行连续读取头部#注释行并剥掉井号和前导空格遇到第一个非注释行就退出。生成的 Markdown 用 pandoc 转 PDF 时加--highlight-styletango保留代码高亮、--listings控制分页避免代码行被页边距截断。这份文档不需要详尽到讲解每个函数能让人照着参数表独立调用、看懂退出码含义交接就已经完成。本文还有配套的精品资源点击获取