☰
Linux运维常用脚本:磁盘告警、日志清理与健康检查自动化
2026/10/8 9:32:01 网站建设 项目流程

简介:面向Linux运维工程师的常用脚本合集,聚焦服务器日常管理、备份、监控与自动化部署等高频场景,可帮助运维人员减少重复手工操作,提升批量维护和故障排查效率。资源包共20个文件,以.sh脚本为主(19个),另附1个nginx.conf配置模板,rar压缩包仅12KB,轻量易用,适合在Linux生产或测试环境中快速部署与二次修改。目前已有82人学习下载。脚本覆盖较广,如MySQL数据库备份(单循环与多循环)、批量创建用户并设置密码、批量主机远程执行命令、LNMP一键部署、PHP与Java项目自动发布、Dos攻击自动屏蔽、MySQL主从同步监控、nginx日志按天切割与分析、网卡流量与系统资源实时查看、磁盘利用率监控、高CPU/内存进程定位、目录变化监控与文件同步等。这些功能贴近日常运维高频场景,适合初学与进阶运维者参考借鉴。

1. Linux运维常用脚本:每天少敲两百次命令,从一份可靠脚本库开始

刚接手一批 CentOS 7 和 Ubuntu 20.04 混合服务器时,我每天大半时间都耗在重复动作上:看磁盘、清日志、重启服务、改配置文件、批量传文件。真正的问题不是不会敲命令,而是同样的操作被反复执行,每次都要重新回忆参数,还要担心漏掉哪台机器。于是我把高频操作收敛成一套 Linux 运维常用脚本,覆盖磁盘告警、日志清理、服务健康检查和批量执行四类场景。这套东西不解决架构问题,但能把日常巡检时间从两小时压到二十分钟,适合刚转行做运维、以及被大量重复工作淹没的初级运维工程师。

脚本的本质是把你脑子里的经验固化成文件,让机器按固定节奏替你干活。下面我会按场景拆开讲,每段都给出可直接落地的脚本和参数说明,并把我在生产环境踩过的坑一并标注出来。

2. 磁盘与日志清理脚本:先保住磁盘,再谈稳定性

2.1 磁盘告警脚本:用 df 和 inode 双指标判断真实风险

磁盘满是最常见的故障诱因,但只看df -h会漏掉一类隐蔽问题:inode 耗尽。当服务器上小文件数量暴增(比如临时文件、邮件队列、容器日志碎片),即使空间还有剩余,文件系统也无法创建新文件。我一般会同时检查空间使用率和 inode 使用率,任一超过阈值就告警。

#!/bin/bash # 磁盘空间与 inode 双维度告警脚本 # 使用方法:配合 crontab 每 10 分钟执行一次 THRESHOLD=85 # 空间使用率告警阈值(%),按业务调整 INODE_THRESHOLD=85 # inode 使用率告警阈值(%) df -P | awk 'NR>1 && $5+0 > '"$THRESHOLD"' {print "空间告警:", $6, $5}' df -iP | awk 'NR>1 && $5+0 > '"$INODE_THRESHOLD"' {print "inode告警:", $6, $5}'

这段脚本的关键是df -P参数,它强制以 POSIX 格式输出,避免超长挂载点换行导致awk解析错位。NR>1跳过表头,$5+0把百分比字符串转成数字。告警消息可以直接接mailx或写入/var/log/disk_alert.log,生产环境建议走企业微信或钉钉机器人 webhook,别依赖服务器本地邮箱,很多最小化安装根本没配邮件服务。

另一个容易翻车的地方是根目录/和/boot分区。/boot空间很小,内核更新几次就满,而系统不会自动清理旧内核。我习惯把/boot单独加入监控列表,阈值降到 70%,并及时提醒yum autoremove或apt purge旧内核。

2.2 日志清理策略:别用 find -delete 直接上生产

日志文件是磁盘增长的最大推手,尤其 Nginx、Tomcat、应用自定义日志。最稳妥的做法是交给 logrotate 按天切割、按周清理,但很多中小团队根本没配 logrotate,日志无限增长,最后只能手动砍。

#!/bin/bash # 按天清理超过 N 天的日志文件,保留 .log 和 .log.1.gz 最新两轮 LOG_DIRS="/var/log/nginx /var/log/tomcat /data/applogs" RETENTION_DAYS=30 for dir in $LOG_DIRS; do if [ -d "$dir" ]; then find "$dir" -type f -name "*.log" -mtime +$RETENTION_DAYS -delete find "$dir" -type f -name "*.gz" -mtime +$RETENTION_DAYS -delete echo "[$(date '+%Y-%m-%d %H:%M:%S')] cleaned $dir" fi done

-mtime +30匹配修改时间超过 30 天的文件,-delete直接删除。这条命令在大多数场景可用,但有三个坑:

  • 不要对 NFS 挂载目录直接-delete,网络文件系统上 find 删除遇到 IO 抖动会报错中断,建议先-print生成清单再批量rm。
  • 文件名含空格或特殊字符时,-delete没问题,但如果先ls再管道给xargs rm,必须用-print0和xargs -0。
  • 应用还在写日志时删除正在写的文件,文件句柄不释放,磁盘空间不会真正回收。需要让应用重新 open 日志文件,常见做法是/usr/bin/truncate -s 0先清空再谈删除,或者配合kill -USR1让 Nginx 重新打开日志。

表:日志类型与推荐保留周期

日志类型保留天数处理方式注意事项
Nginx 访问日志7~15logrotate 切割+压缩按天切割,避免单文件过大
应用业务日志30~60按天分目录归档按服务维度分开目录
系统安全日志90~180压缩存档审计需要,保留时间长
临时调试日志3~7直接清理开启 debug 后必清

3. 服务健康检查与自愈脚本:把「探测→处理→通知」串成闭环

3.1 端口探测脚本:不迷信 systemd,自己维护兜底

systemd 的Restart=always能在进程崩溃时拉起服务,但遇到假死(进程还在、端口不响应、CPU 100% 但请求队列堵死)就无能为力。我习惯在 crontab 里跑一层端口探测,配合健康检查接口,检测失败就执行重启动作。

#!/bin/bash # 检测指定端口 TCP 连通性,失败则重启服务并记录日志 PORT=8080 SERVICE_NAME="tomcat" RESTART_CMD="systemctl restart tomcat" CHECK_TIMEOUT=5 if ! timeout $CHECK_TIMEOUT bash -c "echo > /dev/tcp/127.0.0.1/$PORT" 2>/dev/null; then echo "[$(date '+%F %T')] $SERVICE_NAME port $PORT is unreachable, restarting..." >> /var/log/service_check.log $RESTART_CMD sleep 3 if timeout $CHECK_TIMEOUT bash -c "echo > /dev/tcp/127.0.0.1/$PORT" 2>/dev/null; then echo "[$(date '+%F %T')] $SERVICE_NAME recovered after restart." >> /var/log/service_check.log else echo "[$(date '+%F %T')] $SERVICE_NAME restart FAILED, please check manually." >> /var/log/service_check.log fi fi

/dev/tcp/是 bash 内置的虚拟设备,echo > /dev/tcp/127.0.0.1/8080相当于发起一次 TCP 连接,连接成功则命令返回正常,失败则返回非零。外层timeout 5防止端口处于半开状态时脚本挂死。生产环境注意两点:探活目标别写127.0.0.1,某些服务只在0.0.0.0监听时本地回环也能通,但外部实际不可达;更靠谱的做法是探测域名解析后的真实 IP,或者直接请求应用的健康检查 URI。

另外重启动作要有幂等性设计。脚本被 crontab 每 5 分钟执行一次,如果服务反复崩溃,每次检测到失败都会重启,可能引发重启风暴。我在生产上加了一个计数器:同一小时内重启超过 3 次就不再自动拉起,改为发告警等人工介入。

3.2 三分钟看懂健康检查脚本的退出码设计

脚本能否可靠地融入现有监控体系,很大程度上取决于退出码写得好不好。很多初学者写的检查脚本永远返回 0,监控平台永远显示正常,实际上服务早就挂了。下面这段检查进程数加退出码的写法比较规范:

#!/bin/bash # 检查进程是否存在,并按监控约定返回退出码 # 0=正常, 1=进程不存在, 2=进程数异常 PROC_NAME="nginx" EXPECTED_COUNT=2 COUNT=$(pgrep -f "$PROC_NAME" | wc -l) if [ "$COUNT" -eq 0 ]; then echo "进程不存在: $PROC_NAME" exit 1 elif [ "$COUNT" -lt "$EXPECTED_COUNT" ]; then echo "进程数不足: $COUNT (期望 $EXPECTED_COUNT)" exit 2 fi echo "检查通过, 进程数: $COUNT" exit 0

pgrep -f匹配完整命令行参数,比pgrep nginx更准确——避免只按进程名匹配时漏掉带配置路径启动的实例。wc -l统计匹配行数,这里的一个隐藏坑是:pgrep -f nginx会匹配到执行该脚本的bash进程本身(因为命令行里包含 nginx 字符串),所以结果会额外加 1。解决办法是排除自身:pgrep -f "$PROC_NAME" | grep -v $$或者直接pgrep -x nginx用精确进程名匹配。

配合 Zabbix 或 Prometheus 的custom script监控项时,退出码 1 和 2 会被映射成不同告警级别。这个习惯我从第一年做运维就养成了,现在不管写多小的脚本,出口处一定显式exit,绝不让脚本自然落到底。

3.3 配合 crontab 的定时策略与频率选择

脚本写得再好,调度频率错了照样出问题。我见过有人把磁盘检查设成每分钟执行一次,结果df命令本身开销不大,但后续脚本里如果还包含find日志扫描,那 IO 压力就可能拖垮数据库服务器。

经验值供参考:磁盘和内存检查 5~10 分钟一次,服务端口探活 1~5 分钟一次,日志清理 1 天一次(凌晨低峰期执行),全量配置备份 1 天一次。最关键的是检查脚本本身要轻量,不要在一个周期任务里同时跑 find + 压缩 + 网络转发告警,否则监控脚本就变成了新的故障源。

4. 批量执行脚本:一条命令把操作放到一百台服务器上

4.1 PSSH 与 expect 的取舍:先搞清你的集群有多少台机器

机器少用 expect,机器多直接上 pssh(Parallel SSH),这是我在管理 30 台和 300 台服务器时分别走的两条路。expect 适合处理只有三五台机器、且各机器密码不同的场景;一旦超过二十台,逐个写 expect 脚本本身就是灾难。

#!/usr/bin/expect # expect 批量改密码并执行 uptime 示例 set timeout 10 set ip [lindex $argv 0] set pass [lindex $argv 1] spawn ssh root@$ip "uptime; df -h | head -5" expect { "*password:" { send "$pass\r"; exp_continue } "*yes/no*" { send "yes\r"; exp_continue } eof }

exp_continue的作用是在收到密码提示后发送密码,并继续等待下一个匹配模式。第一次连接时会出现yes/no指纹确认,必须单独处理。这个脚本的问题也很明显:密码明文写在脚本里,而且每次连接都建立新会话,效率很低。超过 10 台机器时连接耗时线性增长,执行完一轮可能要十分钟。

pssh 的写法则完全不同,它维护一个长连接池并发执行,几十台机器并行跑命令也就几秒到十几秒:

# 对 hostlist.txt 中所有机器并发执行 uptime pssh -h hostlist.txt -l root -i "uptime" # 带超时与并发数控制 pssh -h hostlist.txt -l root -t 5 -p 10 -i "df -h"

-p 10控制并发数,默认是 32,网络带宽不足或目标机器性能差时建议调低到 10~15。-t 5是单条命令超时时间,防止某台机器 hang 住拖慢整体。pssh 的配套命令pscp可以用来批量分发文件,-r递归传目录,这比写 for 循环 scp 踏实得多。这里强调一下,pssh 依赖 SSH 免密登录,生产环境必须用密钥认证,密码认证反而容易触发安全审计问题。

4.2 shell 循环加 ssh 的简化实现:小规模机器不引入额外依赖

如果公司安全要求严格,无法安装 pssh,或者只是临时对三四台机器操作,写一个简单的 for 循环也能解决问题。这种方式在「Linux 脚本」语境里最常被搜索到,实现思路是逐个连接并执行。

#!/bin/bash # 用 for 循环对多台机器执行命令 HOSTS="192.168.1.10 192.168.1.11 192.168.1.12" SSH_USER="root" CMD="uptime && hostname && df -h | tail -1" for host in $HOSTS; do echo "======== $host ========" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$SSH_USER@$host" "$CMD" || echo "连接失败: $host" done

-o StrictHostKeyChecking=no避免首次连接交互确认时脚本卡住,-o ConnectTimeout=5让不可达主机在 5 秒内超时,不会白白等待 TCP 默认的 120 秒。这个写法适合一次性任务,优势是零依赖,但有两个硬伤:串行执行,机器多时耗时不可接受;输出混杂,难以区分哪段输出来自哪台机器。中间加个分隔行只能勉强解决辨识问题,真要规模化还是上面说的 pssh 更靠谱,运维脚本的第一原则是顺手且不给自己添乱。

5. 运维脚本避坑指南:三条血泪经验帮你少走弯路

5.1 脚本开头少了 set -e,出错后继续往下跑,后果很严重

我踩过最疼的一次坑:写部署脚本时,前一步rm -rf /data/old_backup因为目录不存在返回了非零状态,但脚本没有set -e,继续执行了下一步移动新包的命令,最后把新包覆盖了旧包,导致发布回滚时找不到可用版本。

#!/bin/bash set -e set -u # 上述两个选项让脚本在出错或变量未定义时立即退出

set -e表示任何命令返回非零状态立即退出,set -u表示使用未定义变量时报错。但注意set -e有例外:if条件、while条件、&&或||连接的左侧命令,这时要用显式判断。我在关键步骤还会加set -o pipefail,确保管道中任何一段失败都被捕获,比如cmd1 | cmd2中cmd1失败但cmd2成功时,最终退出码是 0,pipefail会修正为失败。

5.2 CRLF 换行符导致脚本在 Linux 上执行报错,光看报错完全摸不着头脑

在 Windows 上编辑脚本传送到 Linux 后,bash script.sh会报$'\r': command not found。原因是 Windows 编辑器默认使用CRLF作为行尾,Linux 只认LF。遇到这个报错先别怀疑语法,用file命令或cat -A script.sh | grep '\r'查一下换行符。

# 快速转换 CRLF 为 LF sed -i 's/\r$//' script.sh

这个坑在运维中非常高频,尤其是团队用 Git 跨平台协作时。解决之后建议在仓库里加一个.gitattributes文件,强制文本文件统一用 LF,或者在编辑器里设置按 LF 保存。我遇到过远程主机上脚本权限没问题、执行就是报错的诡异情况,排查半天发现是换行符的问题,当场差点把键盘拍烂。

5.3 脚本里有交互式 sudo 或 su 提示,放到 crontab 里就失效

脚本在终端里手动执行一切正常,放进 crontab 后却报权限错误或找不到命令。核心原因有两个:cron 环境下的 PATH 是最小化的,/usr/local/bin可能不在其中,导致命令找不到;另一个是 sudo 需要 TTY,而 cron 默认没有分配终端。

# crontab 里执行脚本的正确姿势,关键在 PATH 和重定向日志 */5 * * * * /usr/bin/env bash /opt/scripts/check_disk.sh >> /var/log/check_disk.log 2>&1

脚本开头强制定义 PATHexport PATH=/usr/local/bin:/usr/bin:/bin,这样即使 cron 环境不同也能执行。如果需要 sudo,在/etc/sudoers里给对应用户配置NOPASSWD权限,比如ops ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx,而不是在脚本里写echo "password" | sudo -S,密码明文不仅不安全,还可读性极差,别人看到这种脚本会说你运维基本功没练到位。

6. 把常用脚本组织成自己的工具箱:模块化写法与版本管理

6.1 封装一个函数库 script-lib.sh,不同用途的脚本按需加载

等脚本数量超过十几个,平铺文件的方式就不好维护了。我后来把公共函数统一放进一个lib.sh,每个业务脚本开头source /opt/scripts/lib.sh。这样改日志函数或告警函数时,只改一处,所有脚本自动生效。

#!/bin/bash # lib.sh 公共函数库 # 用法: source /opt/scripts/lib.sh log_info() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $*" } log_error() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $*" >&2 } send_alert() { local msg="$1" # 这里接企业微信/钉钉 webhook, 注意 URL 在服务器上保留安全 curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY_FROM_ENV" \ -H 'Content-Type: application/json' \ -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$msg\"}}" }

变量$*是函数所有参数的组合,>&2把错误信息写到标准错误。send_alert的 webhook key 不要写死在库里,建议用环境变量引用,比如${WEBHOOK_KEY:?'webhook key not set'},防止脚本被传到外部仓库时泄密。

公共库的引入还有个好处是统一了退出码。每个脚本都在最后显式标注exit 0或exit $?,配合set -e使用,出错时立即停住,日志里能看到最后一条执行到哪。这套方式和 Ansible 的模块设计思路类似,不追求高级配置管理,但胜在零依赖、一台机器一个 bash 就能跑。

6.2 用 20 分钟做一个自检:跑通一份「新服务器验收脚本」

工具箱建好之后,最好写一个自检流程,避免脚本攒了一堆、真到用时发现某个函数名拼错、或者某段逻辑只在自己机器上跑得通。我每次交付脚本前都会跑一遍下面的验收清单:

#!/bin/bash # 新机器基础环境验收脚本 # 检查 CPU 架构、内存、磁盘、网络连通性、端口监听 echo "===== CPU 信息 =====" lscpu | grep -E 'Architecture|Model name|CPU\(s\)' echo "===== 内存信息 =====" free -h echo "===== 磁盘与文件系统 =====" df -hT | head -10 echo "===== 关键端口监听状态 =====" ss -tlnp | grep -E ':22|:80|:443|:3306|:8080'

df -hT多了一个-T参数可以把文件系统类型也打印出来,排查 NFS 或 overlay 时特别有用。ss -tlnp是netstat的现代替代版,显示监听端口和对应进程 PID,比netstat输出更简洁且性能开销低。如果发现脚本执行时间和预期不符,在脚本外面用time bash xxx.sh测一下,一个巡检脚本跑超过 30 秒就要审视里面的命令是否太低效,是不是被 sleep 卡住了。

工具脚本本身要版本管理。我习惯在脚本头部注释里写# Author: ops; Last Modified: 2025-xx并纳入 Git 管理,每台服务器从 Git 拉取到/opt/scripts,配合 crontab 做定时同步。时间久了自然形成自己的最佳实践库,基本不会出现找不到脚本、不知道脚本改过什么的情况,反而成了新同事入职时最快上手的资料。这是我个人比较推荐的脚本管理方式,希望你也能找到适合自己团队的节奏,这套思路能帮上你就好。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询