Appearance
第2章 Shell 排障与巡检脚本
对应简历:技能第 3 条「能独立完成服务器基础巡检」、技能第 10 条「运用 AI 提升脚本与文档撰写效率」、职责实-3「负责平台日常巡检」、职责运-3「建设并维护 IT 知识库、SOP 与故障处理手册」、项目一 P1-4「输出部署指南与常见问题文档」。 支持版定位:支持岗写脚本的目标不是炫技,是把重复动作固化——巡检交给脚本、信息收集交给脚本、日志扫描交给脚本,人只处理脚本筛出来的异常。 本章保留运维版的四个经典模板,新增"信息收集脚本"和"日志关键字扫描脚本"两个支持场景专用模板。 口径提醒:批量部署/初始化类脚本属于实施交付,本章不展开(面试支持岗被问到,答"批量加载镜像、初始化脚本我写过,骨架和本章模板一致"即可)。
2.1 三剑客排障基本功 【必背】
标准回答:grep、sed、awk 是我日志排障的三件套,分工一句话:grep 负责找到、sed 负责改掉、awk 负责算清楚。 grep 是行过滤,排障我最常用三个参数组合:-i 忽略大小写、-C 3 显示匹配行上下三行——一行报错往往没有调用链,上下文才有因果;-v 反选,grep -Ev '^#|^$' 看配置文件的有效内容是我的开场动作。 awk 是列分析,取列用 $1、$NF 最后一列、-F: 改分隔符,关联数组统计是灵魂:awk '{ip[$1]++} END{for(i in ip) print i,ip[i]}' 一个数组完成分组计数。 sed 是流编辑,改配置用 sed -i.bak 自动留备份,处理"有的行注释有的没注释"用反向引用 sed -ri 's/^#?(Port ).*/\1 2222/'。
在支持场景里这套功夫的价值是"把日志变成结论":错误按小时趋势、TOP 来源 IP、状态码分布、报错上下文,都是一条管道出结果。 我给业务出访问报告、给二线准备证据材料,用的都是这几条命令——三剑客不在于背多少参数,在于能把一个模糊的排查需求立刻翻译成一条管道。
追问链:
- 追问1:统计 nginx 日志里 502 的次数和来源 IP TOP5? → 答:次数用
awk '$9==502' access.log | wc -l,按第九列状态码字段精确判断,比 grep ' 502 ' 更严谨——后者可能匹配到 URL 里恰好含 502 的请求;TOP 来源 IP 用awk '$9==502{print $1}' access.log | sort | uniq -c | sort -nr | head -5,先按状态码筛行再取列统计。这两条我面试手写过不下一遍,注意写的时候先head -1看一眼日志真实格式再定列号——格式不同的日志列号会变,先验证再统计是我的习惯。 - 追问2:找出某时间段内的日志? → 答:按日志时间字段做正则:
grep -E '2026-10-24 14:|2026-10-24 15:' app.log;范围取整段用 sedsed -n '/14:00:00/,/15:30:00/p' app.log——前提是日志严格按时间连续有序。两种我都用:grep 适合"点状匹配",sed 范围匹配适合"取完整时间窗",面试能对比适用条件比只背一个强。大日志先切时间窗再分析,是我在银行处理巨型日志的固定第一步。 - 追问3:awk 关联数组统计的本质是什么? → 答:本质是"以统计维度为下标做计数":
awk '{s[$9]++} END{for(c in s) print c,s[c]}'里 s[$9] 是"以状态码为 key 的字典",每读一行给对应 key 加一,END 块遍历输出。把下标从状态码换成 IP、URL、时间片,就是不同的统计报表——掌握这一个模式,所有"按 X 分组计数"的题都是同一把钥匙。我给团队培训时就说:三剑客不要背 N 道题,要背一个模式加几个参数。 - 追问4(深挖):sed -i 直接改文件有什么风险? → 答:-i 直接改写源文件,正则写错就把配置改坏且无撤销。我的规范两步走:要么
sed -i.bak让它自动留备份,要么先不加 -i 跑一遍看输出、确认替换符合预期再落盘;批量改多台时先在一台验证、diff 对比再推广——这在银行客户环境是变更纪律不是可选项。配合反向引用处理注释行、处理完grep回验,这套"改前备份、改后验证"的小动作,是我批量改 15 台国产虚机配置零事故的原因。
关联话术:"支持岗的三剑客用法是'证据加工':把原始日志加工成给客户看的结论、给二线看的证据、给领导看的趋势——银行驻场时我的巡检日报和故障报告里的每个数字,都是这几条管道算出来的。 后来我把常用管道沉淀成脚本(2.3、2.4),同事们不用记命令,跑脚本就出报告。"
实操模板(排障高频管道,背熟):
bash
grep -C 3 -i error app.log # 报错+上下文
grep -Ev '^#|^$' /etc/ssh/sshd_config # 配置有效行
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head # TOP10 IP
awk '{s[$9]++} END{for(c in s) print c,s[c]}' access.log # 状态码分布
grep ERROR app.log | awk '{print substr($1,1,13)}' | sort | uniq -c # 错误小时趋势2.2 服务器巡检脚本(单机 + 批量)【必背】
标准回答:巡检脚本是支持岗的第一个自动化对象,我有单机和批量两个模板,设计思想一致:"每项检查产出一行结论,异常行带标记"。 单机巡检覆盖六项:负载(uptime 与核数比较)、内存(free 的 available 占比)、磁盘(df -h 和 df -i 双查、超 85% 标记)、失败服务(systemctl --failed)、关键服务进程(pgrep 判断)、近期错误日志(journalctl -p err 按天过滤)——输出带 [WARN] 标记的行就是巡检报告的异常清单。 批量版在单机逻辑外套一层 for 循环加 ssh:每台机器加 -o ConnectTimeout=5 防止失联节点拖死全局,命令包在 || echo unreachable 里保证"一台失败不拖死整体",全部输出 tee 到带日期的日志留档。
这两套脚本我在银行项目跑过近两年:15 台国产虚机每天两次巡检,脚本跑完我只看 [WARN] 行,异常的处理时间从"挨台登录看"压缩到"看一眼报告"。 巡检脚本化的更深层价值是标准统一——人来巡检会漏项、会受情绪影响,脚本每项必查、每次口径一致,这正是 SOP 的落地形态。
追问链:
- 追问1:批量巡检时有一台 SSH 连不上怎么办? → 答:两个保护必须内置:
ssh -o ConnectTimeout=5限制等待时间——默认没有超时的话失联节点能把整个脚本挂住几十秒到几分钟;每台的处理包在|| echo "$host unreachable"里,失败记录一行继续下一台。产出统一 tee 到带日期的日志,跑完grep -i fail\|unreachable得到失联清单,顺手就是一张待处理工单表。我在银行项目跑批量巡检时个别节点维护关机是常态,这个设计保证报告永远能出来,还能自动暴露失联节点。 - 追问2:怎么防止巡检脚本被重复执行? → 答:flock 文件锁:crontab 行写成
flock -n /tmp/patrol.lock -c '/opt/scripts/patrol.sh',-n 拿不到锁直接退出——上一轮还在跑时新触发自动放弃。虽然巡检脚本通常跑得快,但这个习惯要推广到所有定时脚本:备份脚本尤其需要——全量备份跑 40 分钟、cron 每 10 分钟触发一次,没有锁就是两个实例同时写文件。这个细节说出来很加分,因为它是真实事故才会教给人的。 - 追问3:巡检脚本的输出怎么变成可追溯的记录? → 答:三个动作:输出统一带日期戳 tee 到 /var/log/patrol_$(date +%F).log,天然按天归档;报告头部打时间戳和主机名,方便跨机汇总;异常行固定 [WARN] 前缀,汇总时
grep -h WARN patrol_*.log就能拉出全周期异常清单做趋势。我在银行项目的巡检日报就是脚本输出加人工备注两部分——脚本管客观事实,人管解释和结论,两者结合才是完整报告。纯人工抄表慢且易漏,纯脚本又缺上下文判断,这是实践出来的平衡。
关联话术:"巡检脚本是我支持工作'降本'的第一步:银行项目从人工巡检到脚本巡检,每天两次、15 台机器,省下的时间转去做 FAQ 和培训;这也成了我后来建 AI 知识库的动机——先是把'查的动作'自动化,再把'答的问题'自助化,思路是一条线的。 "
实操模板(单机巡检脚本,可默写):
bash
#!/bin/bash
# patrol.sh —— 单机巡检:异常行带 [WARN]
CORES=$(nproc)
[ $(awk '{print $1}' /proc/loadavg | cut -d. -f1) -ge $CORES ] && echo "[WARN] load high: $(uptime)"
free=$(free | awk '/Mem/{printf "%.0f", $7/$2*100}')
[ $free -lt 20 ] && echo "[WARN] mem available ${free}%"
df -h | awk '$5+0>=85 && NR>1{print "[WARN] disk:",$6,$5}'
df -i | awk '$5+0>=85 && NR>1{print "[WARN] inode:",$6,$5}'
systemctl --failed --no-legend | grep . && echo "[WARN] failed services above"
pgrep -f "java" >/dev/null || echo "[WARN] java process not found"
journalctl -p err --since "-1 day" --no-pager | tail -5
echo "== patrol done: $(date '+%F %T') =="bash
#!/bin/bash
# batch_patrol.sh —— 批量巡检:一台失败不拖死全局
for host in $(cat /opt/scripts/iplist.txt); do
echo "===== $host ====="
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no $host \
'bash -s' < /opt/scripts/patrol.sh || echo "[WARN] $host unreachable"
done | tee /var/log/patrol_$(date +%F).log2.3 信息收集脚本(转派二线/原厂的标准动作)【必背】
标准回答:信息收集脚本是支持岗特有的关键模板——问题升级转派给二线或原厂时,对方第一句话往往是"把环境信息收集一下发过来",收集得全不全、快不快,直接决定协同效率。 我的脚本按"一机一包"设计:系统信息(主机名、内核、发行版、uptime、时间同步状态)、资源快照(CPU 负载、内存、磁盘与 inode、TOP 进程)、网络状态(网卡 IP、路由、监听端口、连接状态统计、网关连通性)、服务状态(失败服务清单、目标服务进程与端口)、日志摘录(journalctl 近一天错误、目标应用日志尾部与关键字行),全部落进一个以"主机名_日期"命名的目录,最后 tar 打包。 附带的 tarball 直接作为转派附件,二线拿到就有完整现场。
这个脚本的设计原则有三条:一是只读不写——收集信息绝不能改变系统状态,所有命令都是查询类;二是脱敏出口——打包前把已知敏感字段(IP 网段、账号)做替换处理,金融客户环境对外发文件必须先过脱敏这一关,这是我在银行养成的硬纪律;三是命名可追溯——文件名带主机名和日期,多机多天不出乱。 面试讲这个脚本,重点不是逐行背代码,而是讲清"为什么支持岗需要它":转派质量决定闭环速度,信息收集是转派的前置工序。
追问链:
- 追问1:为什么信息收集要在处理动作之前做? → 答:因为很多处理动作会改变现场——重启进程会清掉内存状态、清日志会销毁证据、改配置会覆盖原值。支持纪律是"先取证再动手":哪怕问题看起来一眼能修,也先把现场信息收下来,处理坏了还有得回溯,处理好了证据归档。我在银行现场吃过这方面的规范红利:一次平台异常,正是因为处理前留了完整快照,华为二线两天后要"当时进程的启动参数和端口状态"时我十分钟就交付了——现场是消耗品,先存后用。
- 追问2:收集的信息里有哪些敏感内容,怎么脱敏? → 答:高风险的是 IP 与网段(暴露内网拓扑)、账号名与主机名(暴露命名规范)、证书与密钥路径内容、业务日志里的用户数据。脱敏做法是打包前用 sed 对文本文件做替换:内网 IP 统一替换成 10.x.x.x 形式的占位符,账号替换成 appuser 这类泛化名;密钥类文件压根不收,只收集路径。发出去之前再用 grep 自查一遍敏感模式。金融客户对文件外发有审计要求,脱敏不是礼貌是合规——我把"脱敏后外发"写进了交付 SOP,这也是面试里证明保密意识的实料。
- 追问3:脚本收集哪些网络信息,为什么? → 答:网卡 IP 和路由(确认地址配置与默认网关)、监听端口 ss -lntup(确认服务是否在听、听在哪个地址)、连接状态统计(ESTABLISHED/TIME_WAIT/CLOSE_WAIT 分布,一眼看出连接健康度)、ping 网关(验证三层可达)、DNS 解析测试(验证解析是否正常)。这五项正好覆盖网络排查命令树的本机证据层——二线拿到就能判断问题在本机配置还是链路对端。信息收集的本质是"替二线先跑一遍排查树",我收集得越标准,转派包被退回重收的次数越少。
关联话术:"信息收集脚本是我和华为原厂协同两年磨出来的工具:原厂工程师按他们的 checklist 要信息,我把要求反编译成脚本,一次收集齐、脱敏后打包——转派包的一次交付合格率明显提升,疑难问题的闭环时间从按天缩短到按小时。 这个脚本的思路我也会带到新团队:转派不是甩锅,是把问题连证据一起递过去。"
实操模板(信息收集脚本,可背结构):
bash
#!/bin/bash
# collect_info.sh —— 一键收集系统信息打包(转派二线/原厂用,只读不写)
OUT=/tmp/info_$(hostname)_$(date +%F_%H%M); mkdir -p $OUT
{ hostname; uname -a; cat /etc/os-release; uptime; timedatectl status; } > $OUT/sys.txt
top -bn1 | head -15 > $OUT/cpu.txt; free -h > $OUT/mem.txt
df -h > $OUT/disk.txt; df -i > $OUT/inode.txt
ps aux --sort=-%cpu | head -10 >> $OUT/cpu.txt
ip -br a > $OUT/net.txt; ip r >> $OUT/net.txt
ss -s > $OUT/ss.txt; ss -lntup >> $OUT/net.txt
ping -c 3 $(ip r | awk '/default/{print $3;exit}') >> $OUT/net.txt 2>&1
systemctl --failed --no-legend > $OUT/svc.txt
pgrep -af "java|nginx|mysqld" > $OUT/proc.txt
journalctl -p err --since "-1 day" --no-pager | tail -100 > $OUT/journal_err.txt
tail -n 200 /var/log/app/app.log > $OUT/app_tail.txt 2>/dev/null
grep -iE "error|exception" /var/log/app/app.log | tail -50 >> $OUT/app_tail.txt 2>/dev/null
# 脱敏:内网IP统一占位(按客户规范调整)
sed -ri 's/\b10\.[0-9]+\.[0-9]+\.[0-9]+\b/10.x.x.x/g' $OUT/*.txt
tar czf ${OUT}.tar.gz -C /tmp $(basename $OUT) && rm -rf $OUT
echo "打包完成:${OUT}.tar.gz(外发前请按保密规范复核)"2.4 日志关键字扫描脚本 【必背】
标准回答:日志关键字扫描脚本解决的是"多文件、多关键字、要汇总"的场景:巡检时逐个文件 grep 效率低还容易漏,用户的报障又常常描述不清"哪个系统什么现象",这时候一把扫描脚本横扫所有日志目录,几秒钟给出"哪个文件、什么关键字、多少条、样例长什么样"的汇总。 我的脚本设计四层:关键字清单可配置(ERROR、Exception、FATAL、timeout、refused 等默认集加临时追加的自定义词);扫描范围可传参(默认 /var/log 加应用日志目录,-xdev 防跨文件系统);输出两层——每个文件每关键字的计数表加每个关键字的最近几条样例行,计数看规模、样例看性质;时间可过滤,只扫当天或最近 N 小时,避免翻旧账。
它和 2.2 巡检脚本的关系是互补:巡检脚本回答"系统本身健康吗",扫描脚本回答"业务在日志层面有没有异常"——很多业务异常系统指标完全正常,只有日志里有痕迹。 我在银行驻场时,客户报"平台有点慢但说不上来哪里不对",扫描脚本两分钟把 error 趋势和超时分布拉出来,定位就有方向了——这类"模糊报障"是支持岗的日常,扫描脚本就是把模糊变清晰的第一工具。
追问链:
- 追问1:扫描结果怎么判断严重程度? → 答:三个维度合成判断:数量维度——某关键字计数突然翻倍(和昨天或上一轮对比)就是异常信号,所以脚本输出保留计数、对比交给趋势;样例维度——看最近样例是同一错误重复(单点问题)还是多种错误并发(系统性问题);关联维度——error 后面跟的组件名、超时目标,直接给出下一步往哪查。我的输出习惯是"计数表 + 样例行"一起给,只给计数没有样例的报告没法行动。扫描是发现,判级要靠人——脚本把人从"翻日志"解放出来做"读结论"。
- 追问2:日志目录很大,扫描会很慢吗?怎么优化? → 答:三个优化:时间过滤优先——用 find -mmin 只圈最近 N 小时修改过的文件,绝大多数场景能把范围缩掉九成,这是收益最大的一步;压缩历史用 zcat 流式处理不落地解压;扫描动作本身 grep 是流式的不会撑内存,瓶颈在 IO,量特别大时错峰跑或拆目录并行。我的实战习惯是"日常扫增量、排查扫全量"两档——日常版本带 -mmin -1440 只扫当天,紧急排查才放开范围。工具是死的,扫描范围的选择体现的是排查思路。
- 追问3:怎么把扫描脚本变成告警而不只是报告? → 答:在脚本尾部加阈值判断:某关键字计数超过预设阈值就往告警通道推一条——低成本方案是写进巡检报告标 [WARN],进阶方案是 curl 一个 webhook 推到企业微信群或钉钉群。我在公司内部 IT 支持的做法是:巡检脚本和扫描脚本各产出一份 [WARN] 清单,汇总推送给我和一个共享群,异常当天必有人认领——"脚本发现、群里认领、工单闭环"这三步,就是把脚本从工具升级成机制。单机工具的价值有限,进了流程才是支持体系的一部分。
关联话术:"这个脚本是公司 IT 知识库里我写的最受欢迎的工具之一:同事报'系统好像不对',我不反问一堆问题,先跑扫描,两分钟后拿着数据跟他对话——'错误集中在 XX 模块、最近一小时 47 条、样例是超时',报障的人立刻知道我懂了。 支持岗的沟通效率,一半是工具给的。"
实操模板(日志关键字扫描脚本,可背结构):
bash
#!/bin/bash
# scan_log.sh —— 多文件关键字扫描:计数表 + 样例行
KEYS=${KEYS:-"ERROR|Exception|FATAL|timeout|refused"} # 可用环境变量覆盖
DIRS=${1:-"/var/log /opt/app/logs"}
HOURS=${HOURS:-24}
FILES=$(find $DIRS -xdev -type f \( -name "*.log" -o -name "*.out" \) -mmin -$((HOURS*60)) 2>/dev/null)
echo "== 扫描范围:最近 ${HOURS}h,$(echo "$FILES" | wc -l) 个文件 =="
printf "%-40s %-10s %s\n" "文件" "关键字" "计数"
for f in $FILES; do
for k in ERROR Exception FATAL timeout refused; do
case ",$KEYS," in *",$k,"*) ;; *) continue;; esac # 只扫清单内关键字
c=$(grep -ci "$k" "$f" 2>/dev/null)
[ "${c:-0}" -gt 0 ] && printf "%-40s %-10s %s\n" "$(basename $f)" "$k" "$c"
done
done
echo "== 各关键字最近 3 条样例 =="
for f in $FILES; do grep -iE "$KEYS" "$f" 2>/dev/null | tail -3; done | head -302.5 服务存活检测与自愈脚本 【必背】
标准回答:服务存活检测解决"服务挂了没人知道、知道了还要人手拉"的问题,我的模板逻辑三段:检测——用 ss -lnt | grep :$PORT 判断端口是否有监听(检测端口比检测进程名更贴近用户真实路径:进程在但端口起不来,服务照样不可用);自愈——不监听就 systemctl restart 拉起,等待几秒复检;升级——复检还不通就写 FAILED 日志并触发告警(curl webhook 或邮件),绝不再无限重启。 "只尝试一次,失败交给人"是我的核心原则:无限重启会掩盖配置错误这类根本问题,还可能把"稳定复现的故障"伪装成"随机故障",刷爆日志。
这个脚本配合 crontab 每 2 分钟跑一次,就是最轻量的看门狗——没有监控系统的小环境,它是保底;有 Prometheus 告警的环境,它是"告警到恢复之间"的自动止血层,缩短 MTTR。 我在公司内部 IT 和银行项目都用过这个模式:银行项目里把启停动作写进标准脚本是为了规范,把检测自愈写成 crontab 任务是为了在没人盯的时间段兜底。
追问链:
- 追问1:自愈脚本重启也拉不起来怎么办? → 答:走升级路径而不是再试:二次检测失败就落 FAILED 日志、触发人工告警,脚本到此为止。因为拉不起来大概率不是"进程死了"而是"起不来"——配置错、依赖挂、端口被占,这些靠重启永远解决不了,反复重启反而制造干扰。升级告警里要带上下文:服务名、检测时间、重启动作已执行、失败状态,让接手的人不用从零开始。这道题真正考的是故障升级意识:自动化负责确定性动作,人负责判断性动作,边界清楚系统才可靠。
- 追问2:为什么检测端口而不是检测进程? → 答:因为"进程在"不等于"服务可用":配置错误、端口被占、依赖加载失败时进程可能在但端口起不来,用户访问照样失败。端口是用户真实访问的入口,检测入口才是端到端视角。更完整的是三层递进:进程在(ps)、端口在(ss)、响应对(curl 一个健康接口返回 200)——我的脚本用端口做闸门,关键服务再补 curl 校验响应内容。这个设计原则后来我做监控告警配置也是同一逻辑:探针要贴近用户路径。
- 追问3:自愈和监控告警什么关系,会不会冲突? → 答:不冲突,是分工:自愈管"已知可自动恢复的故障模式"(进程意外退出、内存波动导致的服务假死),告警管"需要人判断的事"(自愈失败、反复触发、指标异常)。要防的是"自愈成功但没人知道"——所以自愈动作本身必须写日志并可统计,如果某个服务一个月自愈十次,说明它有深层问题,该转人工排查了。我的口径:自愈是止血带不是治疗方案,频繁触发就是升级信号。这个认识来自我统计过自愈日志的实践,不是想当然。
关联话术:"看门狗脚本是我给公司内部环境上的第一个自动化兜底:夜里有服务悄悄挂掉,以前是早上用户来报才知道,上了脚本后半夜自动恢复、早上我只看一条'2:17 自愈成功'的记录——用户体验的差异就在这里:好的支持是用户感觉不到故障。 "
实操模板(存活检测自愈,可默写):
bash
#!/bin/bash
# watch_app.sh —— 端口不通则拉起一次,仍不通告警;crontab */2 * * * *
APP="nginx"; PORT=80; LOG=/var/log/watch_app.log
if ss -lnt | grep -q ":$PORT "; then exit 0; fi
echo "$(date '+%F %T') $APP down, restarting" >> $LOG
systemctl restart $APP; sleep 3
if ss -lnt | grep -q ":$PORT "; then
echo "$(date '+%F %T') $APP recovered" >> $LOG
else
echo "$(date '+%F %T') $APP restart FAILED, escalate" >> $LOG
curl -s -X POST -H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"告警:$APP 端口$PORT 恢复失败,请人工介入\"}}" \
https://webhook.example.com/hook >/dev/null # 告警通道按实际配置
fi2.6 日志清理与备份脚本 【理解】
标准回答:这两类脚本支持岗也要会写会讲。 日志清理脚本的核心纪律是"先预览后删除":find "$DIR" -type f -name "*.log" -mtime +7 -ls 先列出将删清单让人确认,再换 -delete 真删;大量文件时用 find ... -print0 | xargs -0 rm -f 防文件名带空格被拆碎。 生产版我更推荐 find 按时间删而不是裸 rm 通配——可控性和破坏面完全不同。 备份脚本(数据库全量加 binlog 场景)核心参数是 mysqldump --single-transaction 对 InnoDB 不锁表、--master-data=2 记录位点、输出 gzip、find -mtime +7 -delete 滚动清理、全程日志留痕。 两个脚本共同的工程要求:绝对路径(cron 环境变量极小)、日志重定向(没日志的定时任务等于盲跑)、失败可感知(退出码或告警)。
在支持场景里,清理和备份脚本是"给客户交付的标准配置":我驻场时给客户环境配的每个组件都带 logrotate 和备份任务——交付组件不交付配套的清理备份,等于给未来埋雷。
追问链:
- 追问1:cron 里的 % 为什么要转义? → 答:% 在 crontab 里是特殊字符代表换行,会把命令截断——备份文件名带
date +%F直接写在 crontab 行里,现象就是"日志显示任务触发了却没有任何产物"。解法是转义 %,或更彻底地把整个逻辑封装进脚本、crontab 行只写脚本绝对路径加日志重定向。这是我真实踩过的坑,从此我所有 cron 任务遵守"crontab 只调脚本,不写长命令"的铁律——排查时也少一层转义干扰。 - 追问2:怎么确认定时任务真的执行了? → 答:三步验证法:先看 /var/log/cron(Ubuntu 是 syslog)的触发记录——有记录说明 cron 层正常;再看脚本自己的日志——我强制给每个 cron 任务加
>> /var/log/xxx.log 2>&1,把标准输出和错误都落盘;最后看产物——备份文件、报表的时间戳对不对。经验上 cron 问题一半出在"任务行没加重定向",出了错什么线索都没有。这套三步法我也写进了 IT 知识库,同事的定时任务不跑了先按这个走,大部分自己就能解决。
关联话术:"清理与备份脚本我都是成对交付的:给客户装了产生日志的组件,就同步配 logrotate;有数据落库,就同步配备份任务加恢复演练——'交付即完整'是我做支持的习惯,客户环境里的每个定时任务都带日志留痕,出问题有据可查。 "
实操模板:
bash
#!/bin/bash
# clean_log.sh —— 先预览后删除,7 天前日志
DIR=${1:-/var/log/app}
find "$DIR" -type f -name "*.log" -mtime +7 -ls # 预览
find "$DIR" -type f -name "*.log" -mtime +7 -delete # 确认后删除bash
# crontab:只调脚本+日志重定向(%转义或脚本内生成文件名)
0 2 * * * /opt/scripts/backup.sh >> /var/log/backup.log 2>&1
*/2 * * * * flock -n /tmp/watch.lock -c /opt/scripts/watch_app.sh
30 4 * * * /opt/scripts/clean_log.sh /var/log/app >> /var/log/clean.log 2>&12.7 crontab 五段式与定时排查 【必背】
标准回答:crontab 五段:分、时、日、月、周,* 任意、*/n 每隔 n、- 范围、, 列表;背三个示例:30 3 * * * 每天 3:30、*/5 * * * * 每 5 分钟、0 2 * * * 每天 2 点。 操作命令 -e 编辑、-l 列出,-r 全删没有确认我从来不用。 四个经典坑必须会讲:一是 % 特殊字符截断命令(2.6 已讲);二是 cron 的 PATH 极小,脚本内外全用绝对路径;三是 cron 不加载 profile,需要的环境变量要自己 source;四是排查两步走——/var/log/cron 确认有没有触发,"没触发查 cron,触发了没结果查脚本日志"。 支持场景里定时任务的报障很常见:"备份怎么没跑"——九成的答案是这三类:没触发(cron 配置错或服务没起)、触发了失败(没日志重定向查不到原因)、跑成功了产物被下游覆盖或删了。 按这个三分法问下去,几分钟出结论。
追问链:
- 追问1:脚本手动能跑、cron 里不跑,查什么? → 答:标准的环境差异问题,三步收窄:第一步对比环境——cron 的 PATH 极小且不加载 profile,脚本里的相对路径、命令别名、环境变量全部失效,所以脚本内外必须绝对路径;第二步
bash -x script.sh在模拟环境跑一遍和 cron 日志对比,看在哪一步分岔;第三步给 cron 行加输出重定向,把执行时的报错落盘。九成的原因最后落在路径和环境变量上。这个"手动正常定时异常"的题我必背,因为它是定时任务报障的头号形态。 - 追问2:秒级任务怎么实现? → 答:cron 最小粒度是分钟,秒级三条路:常驻脚本
while true; do ...; sleep 5; done配 systemd service 加 Restart=always 托管——我生产上用这个,稳且复用服务管理经验;systemd timer 的 OnCalendar 支持秒级还能看 journal 日志;再就是专门调度系统。选型顺序 systemd 常驻优先。这个题考的是"知道 cron 的边界并给出替代",比硬造 cron 秒级方案(比如 sleep 30 拼两条)专业得多。
关联话术:"定时任务排查三分法(没触发/触发失败/产物问题)是我在知识库里写的高频 FAQ 之一——公司内部环境的 cron 类报障,同事按这篇自己就能查个八成,我的重复工单量实打实降了。 脚本和知识库连起来用,才是支持提效的完整闭环。"
2.8 脚本健壮性与调试 【理解】
标准回答:我的生产脚本有一套固定骨架和自检清单。 骨架五段:shebang(#!/bin/bash 固定,避免 dash 兼容问题)、set -euo pipefail 安全项(-e 出错即停、-u 未定义变量报错、pipefail 管道任一环失败整体算失败)、参数检查($# 不够就打 usage 退出)、主逻辑、日志留痕(exec 重定向全部输出落文件)。 调试两件套:bash -x 打开执行回显看每条命令的展开过程,是查变量和执行路径的第一利器;bash -n 只查语法不执行,发布前必跑。 陷阱清单我重点防四条:变量不加双引号——rm -rf "$DIR" 才安全,空变量加引号是保命符;单括号 [ 里用 > < 会当重定向符,比较用 -gt/-lt 或 [[ ]];while read 接管道在子 shell 里修改变量无效,用 done < file 重定向写法;脚本加 flock 防重入。
这套东西的来源是真实的交付要求:银行客户会反复重跑交付脚本,"幂等可重跑、失败即停、现场可查"是被客户环境训练出来的底线——脚本可信,人才可信。
追问链:
- 追问1:set -e 有什么例外要注意? → 答:-e 在 if 条件、&& || 链、while 条件里不触发退出,因为这些位置的失败是"被预期"的。由此有个真实坑:某个"允许失败"的命令放在普通位置,-e 让整个脚本意外中断。解法是显式声明意图:允许失败的命令写
cmd || true。这个坑我踩过之后,团队规范里就加了"允许失败的命令必须显式 || true"——安全机制也要理解它的例外,不然它自己会变成新的坑。 - 追问2:写完脚本怎么保证质量? → 答:三层:语法层 bash -n 加 shellcheck 静态检查(它抓的多数是"平时不炸、特定输入才炸"的隐患,比如未加引号的变量);行为层用 --dry-run 模式先打印将执行的命令不真执行——批量删除类脚本这是保命流程;交付层重要脚本互相 review、归档带版本。我在公司内部推过"脚本三件套:shellcheck 过、dry-run 模式、归档带版本",同事拿去用的脚本质量明显整齐——支持岗的脚本是要交接给别人用的,质量标准不能只有自己懂。
关联话术:"我的脚本都带三件套交付:set 安全项、日志留痕、dry-run——银行客户拿脚本反复重跑结果和交付时一致,这种'可信任感'就是支持口碑。 另外我写文档会顺手用 AI 辅助生成脚本注释和使用说明(对应简历技能 10),人负责逻辑正确性,AI 负责文档效率,两者结合交付速度明显提升。 "
2.9 本章案例
- 【真实项目·支持场景】转派华为二线的信息收集闭环(STAR,约 200 字):背景是银行平台一次间歇性接口超时,现象偶发、客户侧无法复现,一线排查两小时未定位,按分级口径需要转华为二线。任务是我负责把转派材料准备到"二线拿到即可开工"。行动:我跑信息收集脚本生成三台涉及节点的现场包(系统信息、资源快照、端口与连接状态、错误日志摘录),按保密规范做内网 IP 占位替换;同时从应用日志按时间窗筛出三次超时发生时刻的上下文,把"什么时候、哪个接口、卡在哪一步"整理成一页现象说明。结果:二线依据超时时刻的连接状态和错误样例,当天把方向收敛到中间件证书握手偶发超时,48 小时内给出修复方案闭环。这次之后,"信息收集脚本 + 现象一页纸"成为我们团队转派的标准包。
- 【真实项目】巡检脚本化的降本(STAR):背景是 15 台国产虚机每天两次人工巡检耗时且口径不一。任务是把巡检标准化。行动:我编写单机巡检脚本(六查项、[WARN] 标记)加批量外壳(ConnectTimeout、失败不拖死、tee 留档),试运行一周和人工结果对照校准阈值,然后全面替代人工抄表。结果:单轮巡检从 40 分钟人工降到 5 分钟复核,异常发现率提升(inode 使用率这类人工易漏项进了必查清单),脚本和阈值口径沉淀进交付文档成为客户认可的标准巡检工具。
- 【课程项目】备份与恢复全流程实验:按 mysqldump --single-transaction + binlog 位点完成全量加增量备份,再模拟误删做恢复演练:测试库导入全备、mysqlbinlog 按位点重放、核对数据。把"备份脚本 + cron 调度 + 日志留痕 + 恢复验证"跑成完整闭环,面试讲备份不再只是参数,是流程。
- 【经典故障】cron 任务"触发了没产物":备份任务日志显示执行了但备份目录是空的。定位:cron 日志有触发记录、脚本无输出——最终发现 crontab 行里
date +%F的 % 被 cron 当换行截断。解决:% 转义,并把整个逻辑封装进脚本、crontab 只调脚本。教学点:定时任务三步验证法(触发记录 → 脚本日志 → 产物)能把这类问题五分钟定位。
章末自测清单(快问快答)
- 三剑客分工一句话? → grep 找到、sed 改掉、awk 算清楚。
- 看配置文件有效行? →
grep -Ev '^#|^$' file。 - TOP10 来源 IP 管道? →
awk '{print $1}' log | sort | uniq -c | sort -nr | head。 - awk 关联数组统计模式? →
awk '{s[$9]++} END{for(c in s) print c,s[c]}',换下标即换维度。 - 巡检脚本六查项? → 负载、内存 available、磁盘与 inode、失败服务、关键进程、近期错误日志。
- 批量巡检防失联拖死的两个保护? → ssh -o ConnectTimeout=5 +
|| echo unreachable不中断循环。 - 信息收集脚本三原则? → 只读不写、打包前脱敏、命名可追溯(主机名_日期)。
- 为什么"先取证再动手"? → 处理动作会改变现场,先收集才能回溯与转派。
- 日志扫描脚本输出两层是什么? → 每文件每关键字计数表 + 最近样例行(计数看规模、样例看性质)。
- 自愈脚本的核心原则? → 只尝试一次重启,失败落 FAILED 并告警交给人,不无限重启。
- 为什么检测端口而不是进程? → 进程在不等于服务可用,端口才是用户真实入口。
- crontab 里 % 的坑? → % 是换行特殊字符截断命令,转义 % 或封装进脚本。
- cron 任务没跑的三分法? → 没触发查 /var/log/cron;触发了没结果查脚本日志(重定向);有产物查下游。
- set -euo pipefail 各管什么? → -e 出错即停、-u 未定义变量报错、pipefail 管道任一环失败整体失败。
- 脚本三件套交付标准? → shellcheck 静态检查过、带 dry-run 模式、归档带版本可追溯。
- 手动能跑 cron 不能跑先查什么? → PATH 与环境变量差异(cron 不加载 profile),全绝对路径。