不喜欢广告? 无广告 今天

CRLF 与 LF 破坏 CI 的换行符问题

发布日期

您的shell脚本在本地可以正常运行,但在CI环境中会因一个神秘的解释器错误而失败。通常原因是CRLF行结束符。了解它们是什么,为什么它们会破坏Linux管道,并学习如何通过.gitattributes和编辑器设置永久解决问题。

CRLF 与 LF:破坏 CI 的行结束符 bug 1
广告 移除?

你在笔记本电脑上运行的shell脚本运行正常。你将其推送到GitHub后,CI系统拾取到它,但构建流程却因一个神秘的错误而崩溃——类似这样的错误。 /bin/bash^M: bad interpreter 或者某个任务以126退出码结束,而没有明显原因。脚本语法正确,你没有修改任何一行代码。但构建过程却失败了。

十次中有九次,问题出在换行符上。具体来说,是Windows风格的CRLF换行符隐藏在Linux系统期望只看到LF的文件中。本文解释了这两个字符实际上是什么,为什么这种差异比大多数开发者意识到的更重要,以及如何彻底解决这个问题。

CRLF和LF实际上意味着什么

每个文本文件都需要一种方式来标记行的结束。在不同的操作系统中,使用了两种控制字符:

  • LF (换行符, \n,十六进制 0x0A) —— Unix和Linux的标准。macOS自OS X以来一直使用它。每行结束使用一个字节。
  • CRLF (回车符 + 换行符, \r\n,十六进制 0x0D 0x0A) —— Windows的标准,源自DOS,而DOS又继承自电传打字机。每行结束使用两个字节。

这些名称源自旧式打字机的物理操作:回车符将打印头移动到行的起始位置;换行符将纸张向上卷动一行。DOS以字面形式编码了这两个操作。Unix选择了最简方案,只保留了换行符。

为什么CI会失败而你的笔记本电脑不会

如果你在Windows上编写代码,并且编辑器将文件保存为CRLF,那么在本地运行时一切正常——Windows工具链可以透明地处理这两种格式。但你的CI流程几乎肯定运行在Linux上,而Linux shell解释器会将 \r 视为一个可打印字符,而不是空白字符。当bash读取一个以 \r\n结尾的脚本行时,它看到的是命令后跟一个尾随的回车符。结果看起来是这样的:

/bin/bash^M: bad interpreter: No such file or directory

^M 错误中的 \r是终端如何渲染

的体现。bash试图执行一个以回车符结尾的shebang行,而显然在该路径下没有对应的解释器。

  • 同样的问题以更微妙的方式出现: \r 带有尾随
  • 字符的环境变量值会静默地破坏字符串比较。 ENTRYPOINT Docker
  • 脚本因shebang被破坏而无法启动。 "setting\r" 而不是 "setting".
  • Python脚本读取配置文件时,会获取到像 \r.

这样的键。

Makefile忽略规则,因为制表符字符后跟

如何检测这个问题

cat -A deploy.sh

在修复任何问题之前,请确认文件确实包含CRLF结尾。几种快速方法: ^M$ cat -A (Linux/macOS) $.

以CRLF结尾的行会在末尾显示

file deploy.sh
# deploy.sh: Bash script, ASCII text, with CRLF line terminators

,而以LF结尾的行只会显示

xxd deploy.sh | grep "0d 0a"

file 命令 0a xxd 或 hexdump

任何匹配结果都确认了CRLF的存在。如果你只看到

在行结尾处,那么你的情况是干净的。

如何修复它

# Install
sudo apt install dos2unix    # Debian/Ubuntu
brew install dos2unix        # macOS

# Convert a single file
dos2unix deploy.sh

# Convert all shell scripts in the repo
find . -name "*.sh" -exec dos2unix {} \;

dos2unix(最快的方法)

sed -i "s/\r//" deploy.sh

安装一次,适用于任何文件:

tr -d "\r" < deploy.sh > deploy_fixed.sh && mv deploy_fixed.sh deploy.sh

sed(无需额外工具)

tr(POSIX安全)

如何预防它:Git配置

A .gitattributes 事后修复是被动的。持久的解决方案是告诉Git如何处理换行符,从而确保问题永远不会进入仓库。

# Normalize all text files to LF in the repo
* text=auto eol=lf

# Explicitly enforce LF for files that must never have CRLF
*.sh text eol=lf
*.bash text eol=lf
Makefile text eol=lf
Dockerfile text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
*.json text eol=lf

# Binary files — do not touch
*.png binary
*.jpg binary
*.gif binary
*.zip binary
*.gz binary

text=auto eol=lf 使用.gitattributes方法(推荐)

提交到仓库的文件会强制所有人使用一致的换行符,无论他们本地的Git设置如何。在仓库根目录添加以下内容: .gitattributesline 命令告诉Git自动检测文本文件并以LF格式存储,无论开发者的操作系统如何。下面的显式行则锁定那些在CRLF情况下会导致运行时错误的文件。

git add --renormalize .
git commit -m "normalize line endings"

在添加或修改

后,对仓库中的现有文件进行重规范化: core.autocrlf 核心.autocrlf 设置(仅本地)

# Windows — convert CRLF to LF on commit, LF to CRLF on checkout
git config --global core.autocrlf true

# Linux/macOS — convert CRLF to LF on commit, no conversion on checkout
git config --global core.autocrlf input

Git有一个全局设置 core.autocrlf ,它控制在检出和提交时的换行符转换。推荐的值取决于你的操作系统: .gitattributes 仅依赖

的问题:它只适用于设置它的机器。一个从未配置过它的贡献者仍然可能推送CRLF文件。而

文件由仓库本身强制执行,因此更加可靠。

Visual Studio Code

编辑器设置要正确配置 settings.json:

{
  "files.eol": "\n"
}

你的编辑器是第一道防线。几个快速设置:

当前文件的换行符在状态栏(右下角)显示。点击它可在CRLF和LF之间切换。要为新文件设置默认值,请在你的 中添加以下内容: JetBrains IDEs(IntelliJ、WebStorm、PyCharm) 前往设置 → 编辑器 → 代码样式并设置 .editorconfig 行分隔符

[*]
end_of_line = lf
insert_final_newline = true

Unix和macOS(\n) .editorconfig 。你也可以在仓库根目录添加一个

大多数现代编辑器原生或通过插件支持

。将它提交到仓库意味着贡献者可以在不进行手动配置的情况下获得一致的默认设置。

  1. 添加一个 .gitattributes 完整检查清单 * text=auto eol=lf 如果你正在标准化一个曾经出现过换行符问题的仓库,应按以下顺序操作:
  2. 使用 .editorconfig 完整检查清单 end_of_line = lf.
  3. 运行 git add --renormalize . && git commit -m "normalize line endings" 并为shell脚本、Dockerfile和配置文件制定明确规则。
  4. 运行 dos2unix 添加一个
  5. 集合 core.autocrlf input 来修复现有跟踪文件。 true 在任何将在Linux上执行的未跟踪脚本上添加
  6. 在Linux/macOS机器上全局设置; grep -rUl $'\r' *.sh 在Windows上设置。
想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?