凌晨两点,我盯着监控屏幕上那台CentOS 7服务器的磁盘使用率——97%。登录上去敲了十几次du -sh *,每次都卡在/var/log目录上。最后用find /var/log -type f -size +500M揪出来一个2.3G的messages-20240315,里面全是rsync连接超时的报错。这就是Shell脚本要解决的问题:你不可能每次都手动翻日志找元凶。
CentOS的Shell环境跟Ubuntu略有不同。默认/bin/sh是bash的软链接,但很多老运维习惯在脚本头部写#!/bin/bash。我见过有人用#!/bin/sh结果在CentOS 6上跑得好好的,迁移到CentOS 7就报[[ : not found——因为[[ ]]是bash扩展,而某些精简版系统里/bin/sh真的指向dash。所以第一行永远写#!/bin/bash,别偷懒。
写磁盘清理脚本,我习惯先定义阈值变量。THRESHOLD=85,然后CURRENT=$(df / | awk 'NR==2 { print $5}' | tr -d '%')。这里有个坑:df输出里百分号跟数字连在一起,用tr删掉。接着判断if [ $CURRENT -gt $THRESHOLD ],注意方括号两边必须有空格,这是新手最容易栽的地方。我见过一个脚本因为[$CURRENT -gt 85]报错,排查了半小时才发现是空格问题。
清理逻辑不能太粗暴。find /var/log -name "*.log" -mtime +7 -exec rm { } \;看着没问题,但万一有正在写入的日志呢?我加了个保护:只删.gz和.old后缀的,再配合lsof检查文件是否被占用。实际写起来是这样:
for f in $(find /var/log -name "*.gz" -mtime +30); do if ! lsof "$f" &>/dev/null; then rm -f "$f" fi done 循环里用lsof判断文件是否打开,没打开才删。这比直接rm安全得多。不过lsof在CentOS最小化安装里可能没有,得先yum install -y lsof。脚本里最好加个检查:command -v lsof &>/dev/null || yum install -y lsof。
还有个细节:脚本执行时间。有次我写了个备份脚本,白天跑的时候把服务器IO打满了,业务报警。后来改成ionice -c2 -n7包裹tar命令,降低IO优先级。再配合nice -n 19降CPU优先级。这些参数在CentOS上很管用,尤其是老旧的机械硬盘机器。
调试Shell脚本,我强烈建议在脚本开头加set -x。它会打印每条执行的命令,排查变量赋值问题特别快。但生产环境记得关掉,不然日志会爆炸。另一个技巧是用bash -n script.sh做语法检查,不执行只分析,能提前发现括号不匹配之类的低级错误。
最后说个真实案例。去年有台CentOS 7的数据库服务器,每天凌晨3点自动重启。查了半个月没找到原因。后来在/etc/cron.daily里发现一个前任运维留下的脚本,里面写了个reboot命令,条件是if [ $(free -m | awk 'NR==2 { print $4}') -lt 100 ]。内存低于100M就重启——问题是那台机器内存本来就只有2G,跑着MySQL,空闲内存经常低于100M。脚本逻辑本身没错,但阈值设得太激进。删掉脚本,问题消失。
写CentOS Shell脚本,核心就三件事:路径写绝对路径,变量加双引号,删除操作加确认。别追求花哨的一行流,可读性和安全性比炫技重要。你写的脚本可能半年后自己都看不懂,更别说交接给同事了。