我维护一个 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 不够,得保证不再复发:
.sh 逐一字节级替换,并补上仓库里缺失的 wrapper 源文件;.gitattributes 钉死规则:*.sh text eol=lf——以后不管谁在什么平台上 clone、提交,仓库里的 .sh 永远是 LF;.sh 不含 CR,从源头报警;替换旧 release 资产时还顺手抓到一个潜伏 bug:checksum 清单里 schema 文件的哈希从来就没对上过。为什么一直没被发现?因为校验流程测的是脚本资产,schema 那行从没人校验过——没人去校验校验和文件本身。发布即验证,发布即发现。
还有个工程细节值得一提:修复过程中我先用 Git Bash 的 sed 's/\r$//' 做替换,静默失败——MSYS 的 text-mode 会在管道里自动做行尾转换,导致你以为在删 \r,实际读进来的早就被转换过,grep -c $'\r' 的计数也会撒谎。最后是用 Python 的 read_bytes/write_bytes 做纯字节替换才可靠。也就是说:在 Windows 上修 CRLF 问题,工具链本身就会被 CRLF 坑。
.gitattributes 不是可选项,是第一天就该有的东西;xxx: invalid option name 或 $'do\r': command not found,先查行尾,再查别的。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。