简介这份PDF面向Linux运维工程师与Shell初学者聚焦日常运维中的自动化脚本实践帮助解决日志排查、服务巡检、文件清理与批量操作等重复性工作。内容涵盖日志过滤与错误统计、ping健康检查、旧文件删除与备份压缩、多文件循环处理、scp远程传输、用户home目录校验、日志实时监控以及自动化建用户、进程检查与kill、服务器初始化配置等典型场景并附有可直接参考的命令片段与脚本写法。资源包共1个PDF文件约100KB体积轻便适合随查随用。目前已有1165人学习下载说明其在运维入门与脚本积累方面具备一定参考价值。读者可借此梳理Shell在日志、网络、文件、用户与进程管理中的常见用法形成自己的运维脚本清单提升排障与批量操作效率。1. 从一份 shell 脚本合集说起运维日常到底在重复什么凌晨两点被告警叫醒登机器一看磁盘/var分区使用率 98%日志文件把盘写满了。手动du -sh一层层找、确认、清理、再验证服务恢复——这套动作你做过多少次Linux 运维的日常很大一部分不是解决疑难杂症而是把重复的检查、清理、备份、巡检动作一遍遍执行。shell 脚本就是把这些动作固化下来的最直接手段不需要额外运行时不依赖复杂框架登录任何一台 Linux 机器就能跑。一份「Linux 运维必备工作常用 shell 脚本」的合集本质上解决的是三件事把高频操作标准化、把容易忘的参数写死在脚本里、把需要人工判断的环节变成可自动执行的流程。它适合刚入行的运维工程师建立自己的工具箱也适合老手把散落在各处的脚本整理成可维护的资产。下面不空谈概念直接按「写什么脚本、怎么写、参数怎么设、哪里会翻车」的顺序拆开讲。2. 运维脚本的四个高频场景与选型逻辑2.1 为什么是 shell 而不是 Python很多新手会问既然 Python 也能做运维自动化为什么还要用 shell答案在于场景边界。shell 脚本的优势是「零依赖 管道组合 直接调用系统命令」。当你的任务是「查磁盘、grep 日志、kill 进程、tar 打包、scp 传输」这类以调用现成命令为主的工作shell 的代码量通常只有 Python 的三分之一而且不需要考虑目标机器上有没有装 Python、装的是 2 还是 3、有没有装 paramiko。反过来如果任务涉及复杂数据结构、JSON 解析、并发控制、错误重试逻辑shell 会写得很痛苦这时候该上 Python。我一般的判断标准是脚本主体是「命令的串联」就用 shell主体是「逻辑的处理」就用 Python。运维脚本合集里绝大多数属于前者。2.2 四类脚本的骨架一份实用的运维脚本合集通常覆盖以下四类每类的写法套路不同场景典型脚本核心命令关键点巡检监控磁盘/内存/负载检查df、free、uptime阈值判断 告警输出备份清理日志归档、过期删除find、tar、gzip时间条件 安全删除批量操作多机执行、配置分发for 循环、ssh、rsync主机列表 失败处理服务管理启停、健康检查、重启systemctl、curl、ps状态判断 幂等这四类的共同点是都需要参数化阈值、路径、主机列表不能写死、都需要日志输出出问题能回溯、都需要退出码方便被 cron 或其他脚本调用。2.3 一个最小可用的磁盘巡检脚本先看一个能直接抄的骨架这是所有巡检类脚本的模板#!/bin/bash # disk_check.sh - 磁盘使用率巡检 # 用法: ./disk_check.sh [阈值百分比默认80] set -euo pipefail # 遇错退出、未定义变量报错、管道错误传递 THRESHOLD${1:-80} # 参数1告警阈值默认80% LOG_FILE/var/log/disk_check.log HOSTNAME$(hostname) log() { echo [$(date %Y-%m-%d %H:%M:%S)] [$HOSTNAME] $* | tee -a $LOG_FILE } # 遍历所有本地挂载点排除 tmpfs 和 docker 相关 df -hP -x tmpfs -x devtmpfs | awk NR1 {print $5, $6} | while read -r usage mount; do percent${usage%\%} # 去掉百分号 if [ $percent -ge $THRESHOLD ]; then log WARN: 挂载点 $mount 使用率 ${usage}超过阈值 ${THRESHOLD}% fi done log INFO: 磁盘巡检完成逻辑说明set -euo pipefail是运维脚本的保命三件套缺了它脚本会在出错后继续往下跑产生连锁误操作。df -hP的-P保证输出格式固定POSIX 格式避免长设备名换行导致 awk 取错列。${usage%\%}是 shell 参数扩展去掉末尾的百分号比sed或cut更快且不启子进程。参数说明THRESHOLD用${1:-80}取第一个位置参数没传就用 80。这个写法比if [ -z $1 ]简洁是 shell 脚本入门必须掌握的默认值语法。日志用tee -a同时输出到屏幕和文件方便手动跑和 cron 跑两种场景。2.4 备份清理脚本的时间条件怎么写清理类脚本最容易翻车的地方是find的时间参数。-mtime 7表示「修改时间在 7 天以前」注意是大于 7 天不是「最近 7 天」。很多人第一次写会搞反结果把刚生成的备份删了。#!/bin/bash # log_clean.sh - 清理过期日志并归档 # 用法: ./log_clean.sh /data/logs 7 set -euo pipefail LOG_DIR${1:?必须指定日志目录} # 不传参直接报错退出 KEEP_DAYS${2:-7} ARCHIVE_DIR/data/archive [ -d $LOG_DIR ] || { echo 目录不存在: $LOG_DIR; exit 1; } mkdir -p $ARCHIVE_DIR # 先归档 7 天前的 .log 文件 find $LOG_DIR -name *.log -mtime $KEEP_DAYS -type f | while read -r f; do gzip -c $f $ARCHIVE_DIR/$(basename $f).gz rm -f $f echo 已归档并删除: $f done # 清理 30 天前的归档文件 find $ARCHIVE_DIR -name *.gz -mtime 30 -type f -delete echo 清理完成逻辑说明${1:?必须指定日志目录}是强制参数语法不传就打印错误并退出比手动判断更省事。归档用gzip -c输出到新文件再rm原文件而不是gzip原地压缩是为了让归档目录和日志目录分离避免误删。-type f限定只处理文件防止目录名恰好匹配*.log时出错。参数说明KEEP_DAYS控制保留天数生产环境建议日志保留 7 到 15 天归档保留 30 到 90 天具体看磁盘容量和合规要求。注意find的-mtime是按 24 小时整数天计算的如果需要更精确比如按小时要用-mmin。3. 批量操作与流程控制for 循环、函数、退出码3.1 批量在多台机器上执行命令运维绕不开多机操作。最朴素的写法是 for 循环加 ssh但直接写会踩一堆坑#!/bin/bash # batch_exec.sh - 批量在多台主机执行命令 # 用法: ./batch_exec.sh df -h hosts.txt set -uo pipefail # 注意这里不用 -e单台失败不应中断整体 CMD${1:?必须指定要执行的命令} HOST_FILE${2:?必须指定主机列表文件} SSH_OPTS-o ConnectTimeout5 -o StrictHostKeyCheckingno -o BatchModeyes success0 failed0 while read -r host; do [ -z $host ] continue # 跳过空行 [[ $host ~ ^# ]] continue # 跳过注释行 if output$(ssh $SSH_OPTS $host $CMD 21); then echo [OK] $host echo $output ((success)) else echo [FAIL] $host: $output ((failed)) fi done $HOST_FILE echo 执行完成: 成功 $success 台, 失败 $failed 台 [ $failed -eq 0 ] # 有失败则退出码非0逻辑说明这里刻意不用set -e因为批量操作中单台失败不应该中断整个流程而是记录后继续。BatchModeyes让 ssh 在需要密码时直接失败而不是卡住等待输入这在 cron 或自动化流程里至关重要。((success))是算术运算注意在set -e下((0))会返回非零导致退出所以这个脚本没开-e。参数说明ConnectTimeout5控制连接超时内网可以设 3跨机房建议 10。StrictHostKeyCheckingno跳过首次连接的指纹确认方便自动化但生产环境更稳妥的做法是提前用ssh-keyscan把指纹写入known_hosts。主机列表文件每行一个主机名或 IP支持#注释。3.2 用函数封装重复逻辑脚本超过 50 行就该抽函数。运维脚本里最常抽的是「日志函数」和「检查函数」#!/bin/bash set -euo pipefail readonly SCRIPT_NAME$(basename $0) readonly LOG_FILE/var/log/${SCRIPT_NAME%.sh}.log log_info() { echo [INFO] $(date %F %T) $* | tee -a $LOG_FILE; } log_error() { echo [ERROR] $(date %F %T) $* | tee -a $LOG_FILE 2; } # 检查命令是否存在不存在直接退出 require_cmd() { command -v $1 /dev/null 21 || { log_error 缺少命令: $1; exit 1; } } require_cmd awk require_cmd df log_info 依赖检查通过开始执行逻辑说明readonly定义常量防止被意外覆盖。${SCRIPT_NAME%.sh}去掉.sh后缀让日志文件名和脚本名对应。log_error把输出重定向到 stderr2这样正常日志和错误日志可以分开收集。command -v是检查命令是否存在的标准方式比which更可移植。参数说明日志路径建议统一放在/var/log/下用脚本名区分。如果脚本会被普通用户执行日志路径要改成用户有写权限的目录比如$HOME/.local/log/。3.3 退出码脚本能不能被复用的关键一个脚本能不能被 cron、被其他脚本、被监控系统调用取决于它的退出码是否规范。约定是0 表示成功非 0 表示失败不同数值可以表示不同失败原因。#!/bin/bash set -euo pipefail # 定义退出码 readonly E_OK0 readonly E_ARGS1 # 参数错误 readonly E_DEP2 # 依赖缺失 readonly E_CHECK3 # 检查未通过 main() { [ $# -ge 1 ] || { echo 用法: $0 目标; exit $E_ARGS; } command -v curl /dev/null || exit $E_DEP if ! curl -sf -o /dev/null http://$1/health; then echo 健康检查失败: $1 exit $E_CHECK fi echo 检查通过 exit $E_OK } main $逻辑说明把所有逻辑收进main函数最后一行main $调用这样set -e对函数内命令生效且退出码清晰。curl -sf的-f让 HTTP 错误码4xx/5xx返回非零退出码-s静默模式不输出进度条-o /dev/null丢弃响应体。参数说明退出码的语义由你自己定义但要写进脚本头部注释方便调用方判断。监控系统通常只关心「0 还是非 0」但如果是被上游脚本调用区分退出码能实现更精细的处理逻辑。4. 避坑与排查shell 脚本最常见的五个翻车点4.1 变量没加引号导致路径含空格时出错现象脚本在测试环境正常生产环境报「文件不存在」但文件明明在。原因变量展开时没加双引号路径里的空格被 shell 当成参数分隔符。解决所有变量引用都加双引号rm -rf $DIR而不是rm -rf $DIR。这条规则没有例外养成肌肉记忆。4.2cd失败后继续执行删除了错误目录现象脚本里cd /data/app然后rm -rf *某次/data/app不存在cd失败但脚本继续结果删了当前目录。原因没检查cd的返回值。解决用cd /data/app || exit 1或者开头就set -e。更稳妥的写法是cd /data/app rm -rf ./*用保证顺序。4.3 cron 里跑脚本报「command not found」现象手动执行正常加到 crontab 就报命令找不到。原因cron 的 PATH 环境变量和登录 shell 不同通常只有/usr/bin:/bin。解决脚本里用命令的绝对路径或者在脚本开头显式设置PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。另一个常见原因是脚本没有执行权限cron 里要用bash /path/to/script.sh而不是直接./script.sh。4.4find -mtime的时间理解反了现象想保留最近 7 天的日志结果把最近的删了。原因-mtime 7是「7 天以前」-mtime -7才是「最近 7 天内」。解决记住是大于、-是小于、不带符号是正好。删除前先用-print预览确认无误再换成-delete。这是血泪经验删库跑路往往就发生在这一步。4.5 管道中的 while 循环变量丢失现象cat file | while read line; do count$((count1)); done; echo $count输出 0。原因管道会创建子 shell循环里的变量修改在子 shell 中父 shell 看不到。解决用重定向代替管道while read line; do ...; done file这样循环在当前 shell 执行变量能保留。或者用shopt -s lastpipebash 4.2让管道最后一环在当前 shell 执行。5. 让脚本真正可用日志轮转、幂等与自检脚本写完能跑只是第一步能长期稳定跑才是运维脚本的价值所在。这里说三个我踩过坑之后固定下来的习惯。日志必须能轮转。脚本自己写的日志文件如果一直追加迟早把磁盘写满——这本身就是个讽刺。要么把日志写到/var/log/下并配置logrotate要么在脚本里判断日志大小超过阈值就截断。我一般会在脚本开头加一段# 日志超过 10MB 就轮转 if [ -f $LOG_FILE ] [ $(stat -c%s $LOG_FILE) -gt 10485760 ]; then mv $LOG_FILE ${LOG_FILE}.$(date %Y%m%d%H%M%S) fi操作必须幂等。脚本被重复执行不应该产生副作用。比如创建目录用mkdir -p启动服务前先检查是否已在运行删除文件前先判断存在。幂等性让脚本可以被安全地重试这在自动化流程里是刚需。脚本要能自检。在开头检查依赖命令、检查必要目录、检查权限不满足条件就明确报错退出而不是跑到一半才失败。下面这个自检模板可以直接复用#!/bin/bash set -euo pipefail check_env() { local missing0 for cmd in awk sed grep find; do command -v $cmd /dev/null 21 || { echo 缺少: $cmd; ((missing)); } done [ $missing -eq 0 ] || exit 2 [ -w /var/log ] || { echo 无日志目录写权限; exit 3; } [ $(id -u) -eq 0 ] || echo 警告: 非 root 执行部分操作可能失败 } check_env echo 环境自检通过逻辑说明check_env把依赖检查集中在一处新增依赖只需改这个函数。((missing))在set -e下要注意如果missing初始为 0((0))返回非零会触发退出所以这里用missing0后先判断再自增或者改用missing$((missing1))更安全。参数说明退出码 2 表示依赖缺失3 表示权限不足和前面定义的退出码体系保持一致。非 root 执行只警告不退出因为有些脚本设计上就允许普通用户跑。最后一个习惯每个脚本头部写清楚用法、参数、依赖和退出码。三个月后回来改脚本的人很可能就是你自己会感谢现在的你。脚本合集的价值不在于数量多而在于每一个都经过生产验证、有明确边界、能被放心调用。我现在的做法是每写一个新脚本先问自己三个问题参数能不能不写死、失败了能不能看出来、重复跑会不会出事。这三个问题答不上来脚本就还没到能放进工具箱的程度。希望帮到你。本文还有配套的精品资源点击获取