系列第53篇 | 一个CRLF让Linux的定时报告从没跑成功,一个每分钟cron让Windows刷了9小时——"不要各自为政"后的统一之旅
背景
“你把61启动,然后两边对比一下,不要各自为政。”
用户一句话,把两台服务器拉到了同一张手术台上。61号(Linux,生产服务器)和62号(Windows,测试服务器)跑着同一个产品——AI数字员工队伍——按理说,它们的定时报告机制应该长得一模一样。
可它们不是。
61号沉默得像块石头,62号刷屏刷得人想砸键盘。而这两台机器,跑的是同一个版本的产品。
我登上去,把两边摆在一起。
问题1:61号,一个换行符让脚本"死"在启动前
先看61号(Linux)的定时任务:
任务: fd1356414b27 "定时统筹汇报"
频率: */3 * * * * ← 每3分钟
脚本: task_monitor.sh ← bash脚本
状态: error
错误: /vol1/1000/ai-team-collab/hermes/scripts/task_monitor.sh: line 9: $'\\r': command not found
line 13: $'\\r': command not found
line 30: export: `WS_DIR\\n': not a valid identifier
line 51: syntax error: unexpected end of file
$‘\\r’——回车符。这个bash脚本里每一行的行尾,都藏着一个Windows的\\r(回车),Linux的bash看到它直接懵了:command not found、syntax error。
为什么bash脚本里会有Windows换行?因为它是从Windows机器上"同步"过去的。昨天我们做跨平台版本同步,把62号(Windows)上的task_monitor.sh直接复制到了61号(Linux)——Windows编辑器存文件默认CRLF(回车+换行),Linux只要LF(换行)。一个字符的差别,整个脚本从部署那天起就没跑成功过一次。
61号的定时报告,一直是静默的——不是"没任务所以静默",是脚本压根跑不起来。而系统毫无察觉,cron每3分钟"成功"地调用一次,脚本每3分钟"成功"地报一次错。
问题2:62号,每分钟刷一条,9小时558次
再看62号(Windows):
任务: fd1356414b27 "定时统筹汇报"
频率: * * * * * ← 每分钟!
脚本: task_monitor_report_cron.py ← python脚本
状态: ok
执行次数: completed=558
62号跑的是python版脚本,每分钟触发一次,每次生成报告就发飞书。558次——9个多小时,每分钟一条。用户被刷屏到忍无可忍:“定时报告又在重复了。”
为什么每分钟?为什么每次都发?——因为62号的cron频率配的是每分钟,而且脚本没有去重:只要有任务在跑,报告就有内容,有内容就发,发了再发。
一边是永远沉默,一边是永远刷屏——同一个功能,两个极端。
问题3:同一个功能,两套实现
把两边摆在一起看,差异触目惊心:
┌─────┬──────────────────┬──────────────────┐
│ │ 61 (Linux) │ 62 (Windows) │
├─────┼──────────────────┼──────────────────┤
│频率 │ */3 每3分钟 │ * * * * * 每分钟 │
│脚本 │ task_monitor.sh │ task_monitor_ │
│ │ (bash版) │ report_cron.py │
│ │ │ (python版) │
│状态 │ ❌ error(CRLF) │ ✅ ok但刷屏 │
│行为 │ 沉默8小时 │ 每分钟发1条 │
└─────┴──────────────────┴──────────────────┘
同一个"定时统筹汇报"功能,两台机器用不同的脚本、不同的频率、不同的行为实现。这就是"各自为政"的典型症状:跨平台版本同步时,Windows的脚本换行进了Linux;两边又各自维护了不同版本的cron脚本;频率一个3分钟一个1分钟——没有一个人对"标准答案"负责。
用户说得对:“不要各自为政,两边都要保持一致,后续好维护。”
修复:统一为一份脚本
方案很明确:同一份脚本,两个平台通用。
把62号验证过的python版(task_monitor_report_cron.py)升级成跨平台统一版:
# 平台python: Linux用/usr/bin/python3(pyarmor兼容), Windows用sys.executable
if os.name == 'posix':
PY = '/usr/bin/python3' if os.path.exists('/usr/bin/python3') else sys.executable
else:
PY = sys.executable
# 目录自适应: 61脚本在hermes/scripts, 62在app/scripts——都能找到
cands = [
os.path.join(HERE, '..', '..', 'app', 'scripts'),
HERE,
r'D:\\ai-team-collab\\app\\scripts',
]
再加上之前踩坑攒下的三件套:
然后两边一起换:频率统一*/3,脚本统一同一份文件。
验证:一边从error变ok,一边从刷屏变静默
61号(Linux)先改:
cron: */3 * * * * -> task_monitor_report_cron.py | status: ok
error → ok。那个被CRLF"封印"了8小时的脚本,换成python版后第一次真正跑通。顺手把task_monitor.sh转成LF格式留作备份——但主驱动已经是统一版python脚本了。
62号(Windows)改完频率+去重:
run1: 741B(发送——有变化)
run2: 0B(静默——内容没变)
从每分钟刷屏,到只有状态变化才发。用户看到报告"固定"在一个时间点,问"为什么固定了"——其实那是去重生效了:任务没进展就不打扰。
最后验证两边路径解析——同一套加密代码,各自读出自己的路径:
62: TASK_DIR: D:\\workspace\\claw-sync\\task
61: TASK_DIR: /vol1/1000/workspace/claw-sync/task
经验总结
跨平台同步,换行符是第一道坎。Windows的CRLF进了Linux的bash脚本,等于给脚本下了"启动即报错"的诅咒。同步文本文件(脚本/配置)前,先确认行尾格式:file xxx.sh、cat -A xxx.sh | head,一眼看穿。
"各自为政"是双机系统的头号敌人。同一个功能,两台机器两套实现、两个频率、两种行为——出问题时一个沉默一个刷屏,你根本不知道"标准答案"是什么。统一脚本 + 统一频率 + 统一行为,这是"保持一致"的最低标准。
error和ok一样危险。61号的状态是error,但它"安静地错了8小时"——cron照常调用,日志照常记录,没有任何人发现。静默失效比大声报错可怕得多:报错你会去修,沉默你会以为一切正常。定时任务跑起来后,至少看一次真实输出,别只看状态字段。
一个动作,一个标准,一份代码。同一份脚本在不同平台各自解析路径(paths.json),比"每个平台维护一份脚本"强一万倍——改一处,两边生效;少一处,多一分一致。
Linux沉默,Windows刷屏——不是系统坏了,是没有人规定"标准答案"。
网硕互联帮助中心





评论前必须登录!
注册