干活久了你会发现,运维工作里最不起眼的“删日志”反而是翻车率最高的操作之一。练气期新手容易莽,上来就rm -rf /logs/*,等到服务起不来、查不了历史故障、甚至误删了数据库binlog,才知道疼。这篇不是讲find命令的用法手册,而是把一套能直接上生产环境的日志删除实操脚本掰开揉碎,从需求边界、命令选型到坑点复盘,完整走一遍。适合刚接手服务器日常巡检、准备写第一个定时任务的新手运维,也适合那些已经写了脚本但半夜经常被告警吵醒的老哥对照自查。
1. 为什么99%的新运维都会在日志清理上栽跟头
1.1 日志堆积不是“磁盘满了”这一个问题
很多新手对日志清理的理解就是“磁盘不够了,删点东西”。但日志堆积的隐患远不止磁盘告警那么简单。
先看最直接的问题:磁盘写满。Linux系统分区分区没有预留空间,日志把inode用完,连touch一个空文件都报No space left on device。更阴的是,有些应用服务崩溃后不停写错误日志,每秒钟几个GB地涨,等你登录服务器想排查时,df -h已经100%,vi都打不开,只能靠dd盲清,非常被动。
第二个问题是日志轮转失效。很多Java应用使用Log4j2或Logback自带的滚动策略,但滚动策略只负责“切分文件”,不负责“删除旧文件”。要是配置里只写了maxHistory没生效,或者应用长期运行未重启,catalina.out这种不会被轮转的日志就会无限膨胀。到后来一个文件几十GB,不仅占磁盘,还会拖慢应用本身的日志写入速度,IO都被它吃掉了。
第三个问题很容易忽略:审计和排障需要“留有痕迹”。日志全删了,等出了线上故障要回溯时间线时,发现关键的中间几天的日志早就被清掉了,只能靠回忆和猜。日志删除的边界不是“能删的都删掉”,而是“保留足够长的回溯周期,同时不让磁盘持续增长”。
1.2 手动清理为什么一定不可持续
练气期新人最常见的操作是接到磁盘告警,然后手动执行:
bash复制rm -f /home/app/logs/*.log
这条命令有四个问题:
- 不区分文件时间,会把所有
.log全部清掉,包括昨天刚出的故障日志,想复盘就没了。 - 不看错误日志。
rm虽然报No such file or directory,但即使只漏掉一个被进程占用的日志文件,磁盘空间也得不到真正回收。 - 没有保留周期,全凭感觉。今天觉得日志没用了就全删,明天觉得心虚又不敢删。
- 无法应对高峰期。地震级的告警集中在凌晨两三点,你可能根本来不及手动清。
所以真正能长期运转的日志清理方案一定是脚本化+定时化的。脚本负责“严格按规则识别文件”,定时任务负责“到点自动执行”,人只需要偶尔看一眼报告的清理结果,这才是运维该有的状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从需求到落地:脚本设计的分层拆解
2.1 先界定边界:哪些日志能删,哪些不能动
写脚本之前,第一件事不是写代码,而是搞清楚“删除对象的边界”。否则脚本越强大,造成的破坏越不可挽回。我给自己定了几条铁律:
- 只处理明确指明的日志目录,比如
/var/log、/home/app/logs、/data/logs,绝不允许用“匹配全盘”的写法。 - 只按文件名后缀过滤,比如
*.log、*.out、*.gz,对没有后缀的目录或文件,宁可保留也不动。 - 必须按修改时间判断,保留最近N天,N天以前的才允许删除。
- 如果目录里混有正在写入的活动日志,它们的
mtime通常是当前时间,按天切割后,几天前的文件自然会被过滤掉,不会删到正在用的文件。
换句话说,脚本的逻辑非常简单:在指定目录下,找出“后缀是.log或匹配正则”的普通文件,并且“最后修改时间早于N天”,然后删除。
2.2 脚本骨架与分层设计思路
把这份脚本拆开看,其实可以分成四个层次:
text复制输入层:目录路径、保留天数、文件名通配符
规则层:按时间、按类型、按路径双重过滤
执行层:删除命令、dry-run开关、日志记录
报告层:统计删除数量、释放空间、输出清理结果
这样的分层有实际意义:新运维拿到脚本后,不需要看懂每行bash,只需改开头的几个变量就能安全使用;而需要扩展时,也能快速定位到对应段落。我见过很多脚本把所有逻辑挤在一行里,看着很酷,但出了问题想排查非常痛苦。
下面给出一个可以直接剪贴使用的基础版本:
bash复制#!/bin/bash
# description: 日志清理脚本,按保留天数删除N天前的日志文件
# author: laolin
# ---------- 输入配置 ----------
LOG_DIRS=(
"/home/app/logs"
"/data/logs"
"/var/log/myapp"
)
RESERVE_DAYS=30
FILE_PATTERN="*.log"
# ---------- 其他配置 ----------
DRY_RUN=false
LOG_FILE="/var/log/cleanup_script.log"
# ---------- 工具函数 ----------
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> "$LOG_FILE"
}
# ---------- 主逻辑 ----------
for dir in "${LOG_DIRS[@]}"; do
if [ ! -d "$dir" ]; then
log_message "[WARN] 目录不存在,跳过: $dir"
continue
fi
if [ "$DRY_RUN" = true ]; then
log_message "[DRY-RUN] 以下文件将被删除(当前仅模拟): $dir"
find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" -print
else
deleted_count=$(find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" -delete -print 2>/dev/null | wc -l)
log_message "[OK] $dir: 删除 $deleted_count 个文件"
fi
done
这段脚本是整篇文章的基石,下面所有讨论都是围绕“在这个基础上怎么加固、怎么防呆、怎么踩坑”。先用它跑通流程,再往下看进阶内容。
3. 删除链路上最容易被忽略的四个技术细节
3.1 find 的 mtime 与 -delete 背后的逻辑
我用find而不是手动循环加rm,核心原因是find一次性完成了“条件匹配”和“删除动作”,中间出错的可能性最小。
-mtime +30表示“修改时间距今超过30×24小时”。注意,find对时间边界取整数天,所以“刚好30天整”的文件可能不会被算进去,边界情况容易误判。如果业务上严格要求按小时保留,可以把-mtime换成-mmin +43200(30×24×60分钟),这样边界更精确,但注意分钟数很大时数字别写错。
-delete动作有一点必须清楚:find的删除顺序是从子目录往顶层删,这个特性保证了“先删内容,再删目录”,不会出现“目录非空删不掉”的问题。但如果目录本身只剩空目录,-delete并不会自动把空目录也删掉。要想连空目录一起清理,可以加-empty -type d -delete这条独立的find命令,不过建议谨慎:有些应用启动时会检查目录是否存在,目录被顺手删空反而会引发新问题。
-print配合-delete的顺序坑也很经典。find的执行模型是“先输出,后动作”,所以-print会在删除前把文件名打出来。我在统计已删除数量时用的是$(find ... -delete -print | wc -l),注意-delete和-print的顺序其实都能统计到,因为wc -l统计的是打印行数,但有的蹩脚脚本写成-print -delete,虽然也打印,语义上容易让人误以为先打印后删除,实际也是先打印后删除,但阅读起来很不直观。统一把-delete放前面,后面跟-print作为计数管道,语义更清楚。
3.2 文件通配符里的隐藏陷阱
脚本里FILE_PATTERN="*.log",我加了引号,这个细节很关键。
如果不加引号,bash会先对*.log做一遍“路径名展开”,假如当前目录正好有一个叫access.log的文件,find命令就变成了:
bash复制find /home/app/logs -type f -name access.log -mtime +30 -delete
看起来不影响,因为find -name会再次匹配。但假如当前目录有多个.log文件,展开后就成了多参数,比如-name a.log b.log c.log,后面几个参数会被find当作路径而非表达式,命令直接报错或者结果非常离谱。更危险的是,如果当前目录下没有匹配文件,bash在默认情况下会保留*.log原样,这没问题;但只要你给shopt -s nullglob开过全局展开,变量本身就是空,命令变成-name后面没参数,同样报错。
所以写脚本时,凡是涉及文件的模式串,一律用双引号包起来,让find自己去解析通配符。这是一个一行字母的教训,但能救你一命。
3.3 删除命令要不要加 2>/dev/null
我见过很多脚本删除时把stderr全部吞掉:
bash复制find "$dir" -type f -name "*.log" -mtime +30 -delete 2>/dev/null
这在平时是省心,但一旦出现权限问题或其他异常,你完全不知道有文件没删掉,日志里也看不出端倪,问题会一直潜伏。我不建议全吞。更好的做法是把删除过程中的错误单独记录:
bash复制find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" -delete -print 2>>"$LOG_FILE" | wc -l
这样正常文件计数进结果,报错信息追加到日志,既不断流,又能留痕。这个习惯在后续排查“为什么某台机器磁盘没降下来”时非常有用。
3.4 --preserve-root 和路径保护:最后一根保险丝
rm -rf /这种事故不是段子,是真有人干过。find命令同样危险:假如变量dir传进来是/,那么find / -type f -name "*.log" -mtime +30 -delete会尝试删掉整个系统里所有30天前的日志文件。听起来不算破坏系统,但加上-name "*.conf"呢?后果不堪设想。
GNU find有个默认开启的--preserve-root选项,禁止对根目录本身执行-delete或-exec rm。但为了给脚本加双保险,我还在开头加了路径校验:
bash复制safe_dir() {
local dir="$1"
[[ "$dir" == "/" ]] && return 1
[[ "$dir" == "/var" ]] && return 1
[[ "$dir" == "/usr" ]] && return 1
[[ "$dir" == "/etc" ]] && return 1
return 0
}
虽然这些顶级目录一般不会被写进配置,但写脚本时多一道校验,等于给“手滑”加了一道护栏。后面我在踩坑实录里会专门讲一次真实的事故,就是路径变量为空时脚本差点把整个系统扫了一遍。
4. 定时任务的配置与执行权限:安全落地的最后一公里
4.1 crontab 怎么写才不出错
脚本本身没问题还不够,配套的定时任务错了同样翻车。先看传统写法:
cron复制0 2 * * * /usr/local/sbin/clean_logs.sh
这是每天凌晨2点执行一次。对日志清理来说,凌晨执行是合理的,业务低峰,I/O压力小,删除动作不容易和业务抢占资源。
但有几个隐藏问题。首先,环境变量问题:cron执行shell时,PATH非常精简,大概率没有/usr/local/bin。如果脚本内部调用了外部命令(比如s3cmd或者python),但没写绝对路径,就会出现“手动执行一切正常,cron执行就像没跑过一样”。解决方式有两种:在脚本头部显式声明#!/bin/bash并导出PATH:
bash复制export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
或者是在crontab里指定绝对路径,并且用/bin/bash显式调用脚本:
cron复制0 2 * * * /bin/bash /usr/local/sbin/clean_logs.sh >> /var/log/cleanup_cron.log 2>&1
这里把stdout和stderr都定向到了文件,方便事后查cron有没有真的执行、报了什么错。
其次,cron不会主动加载你登录shell里的环境变量,比如JAVA_HOME、ORACLE_HOME。如果日志清理脚本里会调用find之外的命令(后面讲的压缩归档就可能用到gzip),最好也统一用绝对路径。在脚本里写死外部命令路径虽然丑,但稳。
4.2 系统目录的轮转:logrotate 与自研脚本怎么配合
有人会问,Linux自带的logrotate不是挺好用的吗?为什么还要自己写脚本?
logrotate确实能处理/var/log底下的很多标准日志,比如syslog、cron、messages。但它做不到两件事:
- 对业务日志做不到“按应用自定义保留周期”。比如A应用留7天,B应用留90天,
logrotate配置多份没问题,但每份都要写对应配置,不够灵活。 logrotate的删除策略是“轮转后删除旧归档”,文件管理粒度很粗,有些场景想针对文件名模式做精细处理,得写sharedscripts加一堆钩子,复杂度就上来了。
所以现实是:系统日志交给logrotate,业务日志交给自研脚本,两者各管一块。自研脚本不碰/var/log/messages,也不碰/var/log/secure,这些系统日志的权限和上下文很敏感,如果用find -delete误删了,恢复了也未必完整。边界必须清晰。
4.3 为什么建议先跑一个月的 DRY_RUN
我强烈建议任何清理脚本都先在服务器上跑至少一周的DRY_RUN模式,理想情况是跑满一个完整的保留周期,比如保留30天就观察30天。
DRY_RUN的意义不是让你看一眼“它大概会删什么”,而是让你熟悉这台机器上日志的真实增长模式。有些日志目录在月初会生成很多临时文件,在月中又会被清理,它们的mtime会跳跃式变化,光看一天的模拟删除列表很容易误判。
同时,DRY_RUN可以帮助你发现“目录里混着不该删的文件”这种问题。我遇到过最典型的情况:某业务目录下不仅有*.log,还有个备份目录,里面文件名也是backup_20241201.log,这其实是数据库导出的备份文件。如果只按后缀名匹配,会被脚本当作日志删掉,而DRY_RUN模式下的输出列表会让你第一时间发现这类漏网之鱼。
5. 一次真实事故复盘:路径变量为空时,脚本差点跑满整个磁盘
5.1 事故现场
我手头有台服务器,某次更新脚本时改动了配置解析部分。结果配置项有个字段没传进来,导致LOG_DIRS数组里某个元素的值为空字符串。清理脚本当时已经上线运行,第二天业务方反馈某目录下的历史日志没有被清理。我登录服务器检查,发现find的执行结果是:
bash复制find "" -type f -name "*.log" -mtime +30 -delete
在无法找到空路径的情况下,find返回了错误,但错误流被我之前用2>/dev/null吞掉了,所以日志里完全看不出任何异常。
更危险的是,脚本里有一个外层循环遍历多个目录的场景:如果某一项目录为空,不会影响后面目录的正常执行,所以整体看起来好像一切正常,只有那一个目录的日志越堆越多。
这还不是最糟的。最糟的是有一次我把变量名拼错,导致解析出来的是/,如果当时没写safe_dir校验,find / -name "*.log" -mtime +30 -delete就会按整个文件系统规则去删除,虽然因为有-name "*.log"约束不至于删系统文件,但会把一些业务跨目录的日志全部误删,到时恢复数据的时间成本就大了。
5.2 复盘后的三个加固措施
事故之后,我对脚本做了三个改动,现在分享出来供你参考。
第一步:把所有日志目录配置从“空格分隔变量”改成“数组显式声明”,并且增加一个配置校验函数:
bash复制validate_dirs() {
for d in "${LOG_DIRS[@]}"; do
if [ -z "$d" ]; then
log_message "[ERROR] 配置中存在空目录项,终止执行"
exit 1
fi
if [[ "$d" != /* ]]; then
log_message "[ERROR] 目录必须使用绝对路径: $d"
exit 1
fi
safe_dir "$d" || {
log_message "[ERROR] 目录在保护名单中,拒绝执行: $d"
exit 1
}
done
}
第二步:不再吞掉stderr,错误信息一律追加到日志文件。这样即使find因为路径为空或权限不足而失败,日志里也会记录得一清二楚。
第三步:每次实际删除之前,先打印一段摘要,把即将删除的计数统计出来,然后随机抽取几个文件名看一眼,再决定是否真正执行删除。“先观察,后动作”这个习惯非常管用。
说到这,可能又有朋友会问:既然脚本跑错了会误删,那要不要做成“先移动到一个回收站目录,观察几天后再真删”的方案?这当然是更稳妥的办法,但代价是磁盘上要有双倍空间,很多小型服务器根本扛不住。我的取舍标准是:如果服务器磁盘空间比较充裕(比如剩余30%以上),日志量不大,就上回收站方案;如果磁盘本身很紧张,回收站方案等于没做。下面展开讲。
6. 进阶思路:从“直接删除”升级到“安全归档+回收站”
6.1 回收站方案的具体操作
简单说,就是脚本不再直接-delete,而是把旧日志移动到一个/data/logs_trash/目录下,定时任务再加一个“清理回收站中大于N天文件”的独立步骤。
bash复制TRASH_DIR="/data/logs_trash"
RECYCLE_DAYS=7
# 移动阶段:把日志放入回收站
find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" \
-exec mv {} "$TRASH_DIR/" \;
# 清空阶段:回收站里超过RECYCLE_DAYS的直接删
find "$TRASH_DIR" -type f -name "$FILE_PATTERN" -mtime +"$RECYCLE_DAYS" -delete
这里有个细节:mv到同一个回收站目录时,不同日期删除的同名文件会互相覆盖。所以移动时最好带上日期标记,改成这样:
bash复制find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" \
-exec mv {} "$TRASH_DIR/$(date +%Y%m%d)_$(basename {})" \;
要注意的是find的-exec里调用basename和date,每个文件都会执行一次子shell,文件数量上万时会明显变慢。如果日志文件量极大,建议改用while read循环处理,但那样脚本复杂度会上升。我的判断是:文件量在几千以内,用-exec没问题;超过几万,就得考虑效率了。对绝大多数业务服务器来说,几千个文件的日志清理场景占绝对多数。
6.2 日志压缩归档的搭配方案
实际生产环境的另一条优化路线是“先压缩再保留”。日志文件可压缩率非常高,常见文本日志用gzip压完往往能省90%的空间。脚本可以这样设计:
- 对于N天前的
*.log,先压缩成*.log.gz,再删除源文件; - 对于30天前的
*.log.gz,直接删除。
前半段可以用:
bash复制find "$dir" -type f -name "*.log" -mtime +7 -mtime -30 -exec gzip {} \;
后半段再补一条:
bash复制find "$dir" -type f -name "*.log.gz" -mtime +30 -delete
这种“压缩窗口+删除窗口”的两级策略,比单纯删文件合理得多。因为它既保证了磁盘空间长期稳定,又让你有时间把“已压缩的旧日志”再拖到离线存档,适合日志需要保留较长时间供审计的场景。
需要警惕的是,gzip命令执行失败会导致处理中断。如果磁盘空间很满,压缩过程中需要临时写文件,此时可能“边压边爆”。所以执行前最好先用df判断剩余空间,低于阈值时就跳过压缩,直接删。这个判断逻辑可以写成独立函数,放在脚本主流程开始前。
6.3 软链接与跨文件系统的处理
日志目录偶尔有软链接指向别的分区,比如/data/logs/old -> /mnt/backup/logs。find -type f默认会跟随软链接吗?不会。find默认不跟随-type f的符号链接,具体行为取决于-L选项。如果不加-L,-type f不会命中指向文件的软链接,所以不会误删软链接指向的目标。这个行为是安全的。
但如果有人习惯给find加-L,就可能顺着软链接跑到别的文件系统里去删文件,给排查带来巨大麻烦。所以我的脚本里明确不加-L,并且会在代码注释里写明“不做符号链接跟随”。
对于跨文件系统的移动,mv会先尝试rename操作,跨文件系统时会自动降级为“复制+删除”,如果文件特别大,这个操作会比较慢。回收站方案如果把目录设在另一个盘,跨盘移动的开销不可忽略,需要评估好。
7. 运维习惯:脚本上线前必须检查的九条清单
每次给新机器部署日志清理脚本,我都会过一遍这个清单,踩过坑之后整理出来的:
-
路径硬编码还是变量化:所有目录都写成变量,并且使用绝对路径。任何相对路径都可能因为
cron的当前目录不在预期位置而出问题。 -
是否受cron环境变量影响:脚本头部显式导出
PATH,不依赖登录shell的环境变量。 -
find是否有保护开关:至少把
/、/usr、/var、/etc加入保护名单,参数化路径不能解析为空。 -
是否有未预期的通配符展开:所有模式串加双引号。
-
是否会误删符号链接目标:不加
-L,不跟软链接。 -
是否要判断磁盘剩余空间并设置阈值:剩余空间低于阈值时先告警,或者跳过压缩步骤。
-
日志记录是否完整:清理了多少文件、释放了多少空间、有没有报错,都要写进独立日志文件。
-
是否预留了dry-run开关:上线前强制走一遍dry-run验证。
-
是否做了crontab执行记录:把cron执行的stdout和stderr定向到独立文件,便于回溯。
这份清单看上去繁琐,但真正出事的时候能省下的时间成本远不止这些。我见过不少同行为了省事跳过第3和第8项,最后都在某个深夜付出了代价。
7.1 磁盘空间检查:脚本的第一道防线
把磁盘检查单独拎出来讲,是因为这个逻辑放在脚本最前面能挡住很多灾难。我常用的写法:
bash复制df -h "$dir" | awk 'NR==2 {print $5}' | sed 's/%//'
更严谨的做法是用df -P(POSIX格式)来避免超长行被换行截断,然后用awk取第5列。脚本里这样判断:
bash复制check_space() {
local dir="$1"
local threshold=85
local usage
usage=$(df -P "$dir" | awk 'NR==2 {print +$5}')
if [ "$usage" -ge "$threshold" ]; then
log_message "[WARN] $dir 磁盘使用率已达 ${usage}%,触发阈值"
return 1
fi
return 0
}
注意awk里+$5会把百分号自动转成数值,避免字符串比较出意外。这种检查在压缩归档方案里尤其重要:如果磁盘已经满了,再执行gzip大日志,压缩后的临时文件会直接写挂分区。先判断,后操作,永远是对的。
8. 日志删除脚本的“可观测性”:不要删完就完事
8.1 清理日志本身也要做轮转
脚本自己会往/var/log/cleanup_script.log写日志,时间长了这个文件也可能变成巨无霸。所以脚本执行完,还要对日志进行简单的轮转处理:
bash复制if [ -f "$LOG_FILE" ] && [ "$(stat -c%s "$LOG_FILE")" -gt 10485760 ]; then
mv "$LOG_FILE" "${LOG_FILE}.1"
fi
只保留一个LOG_FILE.1,不需要保留更多历史,因为这个日志只是用来排查“最近脚本有没有异常”,不是审计归档。
8.2 如何输出“人类可读”的清理报告
这里指的“人类可读”不是输出一大堆路径列表,而是输出关键指标。推荐在脚本最后输出这样一段报告:
text复制[OK] 清理执行完成
时间:2025-01-12 02:00:03
目录数:3
累计删除:1,234 个文件
累计释放:48.2 GB
耗时:26 秒
用脚本统计时,删除文件的大小不能直接用du -sh,因为它统计的是当前文件大小,删除后文件就没了。我通常的做法是在删除前先把待删文件的总大小累计出来:
bash复制reclaim_size=$(find "$dir" -type f -name "$FILE_PATTERN" -mtime +"$RESERVE_DAYS" -printf '%s\n' | awk '{sum+=$1} END {print sum}')
然后删除,在报告里把整个流程串起来。这种做法让每次执行都能输出“释放了多少空间”,对于磁盘趋势分析和容量规划非常有参考价值。
8.3 排障时的第一手线索
脚本执行完,日志里除了成功记录,还要记录一些上下文。比如当时机器负载、磁盘IO状态,甚至当时正在运行的关键进程。我习惯在脚本最后追加一行:
bash复制uptime >> "$LOG_FILE"
df -h >> "$LOG_FILE"
不要觉得这多余。当你某天发现“昨晚清理跑完业务系统变卡了”,对照uptime和df输出能立刻判断出是删除过程导致的IO尖峰还是本来就负载高。这些细节平时不起眼,排障时能让你少走半小时弯路。
9. 我的实操体会:脚本永远只是工具,真正靠的是边界感
日志删除脚本写到最后,其实难度不在shell语法,而在“你能不能给文件划清生死线”。很多新手一上来就想写一个“万能清理工具”,什么日志都能删,删完还能精确报告,结果复杂到连自己都调试不明白。我反而建议从最朴素的版本开始,每个目录单独配置、单独校验、单独观察,跑顺之后再慢慢加压缩、归档、报告功能。
运维工作里有一个很犀利的隐喻:删除日志像是在自己家里收废品,你可以把东西扔掉腾地方,但扔掉之前你得知道哪些是垃圾、哪些是暂时用不到但以后还要翻出来的账本。过于激进会把账本烧了,过于保守家里堆成山。脚本的本质只是把你心里那套“什么该扔、什么该留”的规则固化下来。所以动手写之前,先把规则想清楚,比任何语法技巧都重要。
至于脚本本身,保持可读性永远比炫技重要。不需要一行流,不需要n层管道套娃。能用find一个命令搞定的事别拆成三个子函数,能明明白白输出的日志别吞进黑洞。多年以后你回头看自己写的脚本,能一眼看懂,就是好脚本。
如果后续你想把这套脚本整合到自己的发布流程里,或者想加邮件、企业微信告警通知清理结果,欢迎在评论区和大家一起聊聊。运维路上一个人踩坑太孤单,交流才能互相兜底。
