首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一个让我的开源项目在 Linux 上 100% 装不上的回车符

一个让我的开源项目在 Linux 上 100% 装不上的回车符

原创
作者头像
四王爷
发布于 2026-10-28 15:16:50
发布于 2026-10-28 15:16:50
70
举报

我维护一个 GPU 能耗测量工具(MLCube 容器格式)。昨天一位复现者在干净的 Ubuntu 22.04 上跑"一键脚本",反馈:100% 失败。截图里第一行报错就是标题这句——set: pipefail: invalid option name。

熟悉的人一眼就能看出后面的 \r:这是 Windows 行尾符(CRLF)的经典症状。bash 读到 pipefail\r 时,\r 不是空白字符,于是把 pipefail\r 当成一个不存在的选项名。

排查:比预想的严重得多

我一开始以为只是单个脚本的问题。逐个核对字节后发现问题全在:仓库里 9 个 shell 脚本全部是 CRLF——入口 run.sh(197 行)、quickstart.sh(368 行)、部署用的六个脚本,连站点的"下载器" stub 自己也是 CRLF。源头不是 release 上传时损坏,而是仓库 blob 本身就是 CRLF——在 Windows 上开发、git autocrlf 配置不严谨,行尾就这样一路带进了每个发布资产。

最有讽刺意味的一处:复现者下载完先做了 SHA-256 校验,通过了——然后脚本炸了。原因很简单:校验和就是对着 CRLF 版本算的,文件和校验和"自洽地错"。校验和保障的是完整性(没被篡改),不保障正确性(行尾本身就是错的)。一排错误的字节配上一个匹配错误的校验和,安全校验做得再好也拦不住。

修复:四件事一起做

单把文件转回 LF 不够,得保证不再复发:

  1. 源码全量转 LF,9 个 .sh 逐一字节级替换,并补上仓库里缺失的 wrapper 源文件;
  2. .gitattributes 钉死规则:*.sh text eol=lf——以后不管谁在什么平台上 clone、提交,仓库里的 .sh 永远是 LF;
  3. CI 加行尾守卫:每次 CI 显式检查 .sh 不含 CR,从源头报警;
  4. 重新发布:发布 v2 资产并换绑,v1 保留作墓碑,不悄悄删——让老用户的报错可追溯。

替换旧 release 资产时还顺手抓到一个潜伏 bug:checksum 清单里 schema 文件的哈希从来就没对上过。为什么一直没被发现?因为校验流程测的是脚本资产,schema 那行从没人校验过——没人去校验校验和文件本身。发布即验证,发布即发现。

修 CRLF 时踩的坑:在 Windows 上修 Windows 的问题

还有个工程细节值得一提:修复过程中我先用 Git Bash 的 sed 's/\r$//' 做替换,静默失败——MSYS 的 text-mode 会在管道里自动做行尾转换,导致你以为在删 \r,实际读进来的早就被转换过,grep -c $'\r' 的计数也会撒谎。最后是用 Python 的 read_bytes/write_bytes 做纯字节替换才可靠。也就是说:在 Windows 上修 CRLF 问题,工具链本身就会被 CRLF 坑。

教训

  • 一键脚本是新用户的第一触点,它挂了,别人对项目的第一印象就是"这项目装不上",后面写得再好都没机会展示;
  • Windows 上开发跨平台项目,.gitattributes 不是可选项,是第一天就该有的东西;
  • 校验和能自洽地错——它验证完整性,不验证正确性;
  • 见到 xxx: invalid option name 或 $'do\r': command not found,先查行尾,再查别的。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 排查:比预想的严重得多
  • 修复:四件事一起做
  • 修 CRLF 时踩的坑:在 Windows 上修 Windows 的问题
  • 教训
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档