# env: node\r: No such file or directory

438 阅读4分钟

起因

不同平台处理 End of Line 的方式不同, 是一个大家在跨开发平台合作开发时,经常会碰到的问题。

在再再再再次撞到南墙后,我下定决心去搞懂 LF/CRLF。首先让我们看一个简单的例子。

有些code, 绕了一圈回来就变了

code_sprite.png

上图包含了五个步骤:

  1. 在 Unix like 上开发,写了两行代码, git push 到 github, 此时内容为 Hello\nworld

    Hello
    world
    
  2. 在 windows 上执行 git pull/clone 把代码下载到本地。

  3. 在 windows 上,git 把 LF(\n) 替换成 CRLF(\r\n)

    Hello\nworldHello\r\nworld

  4. 在 windows 本地执行 npm publish, 发布 npm package 到 npm , 此时内容为Hello\r\nworld

  5. 在 Unix like 上 执行 yarn , 此时拉取到的npm包中代码是 Hello\r\nworld

就代码在 Unix like 上的展示来说,无论是 LF 还是 CRLF, 对于我们阅读都是没有任何影响的。但是对下面这个场景来说,这个小改动的影响是致命的。

新的风暴已经出现:#!/usr/bin/env node\r

本来我正在使用 farrow 开发我的项目 farrow-petstore , 开发环境是 mac, 包管理工具是 yarn。在升级 farrow 到 1.8.10 后,yarn farrow dev 启动项目,一个莫名其妙的错误出现了!

env: node\r: No such file or directory

于是用 vscode 打开 node_modules 下 对应的 bin 文件, 然后这个 CRLF 就出卖了它,一切水落石出**。**

Untitled.png

bin 文件在 vscode 中看起来是这样的:

#!/usr/bin/env node
console.log("hello world")

然而实际上它是这样的:

#!/usr/bin/env node\r\nconsole.log("hello world")

当在 Unix like 的环境下执行这个 bin 文件时,\n 被解释为 换行,于是实际上这行代码被 Unix like 环境解释成:

#!/usr/bin/env node\r
console.log("hello world")

node\r 被当作一个命令来看待,于是出现了上述的错误。

细节

End of Line

EOL 是一个控制字符,用于表示一行文本的结束和一个新的文本行的开始。

在打印机时代, 使用 CR 把打印机指针移动到行首,LF 把指针往下移动一行。Windows 继承了 这种做法,所以 Windows 中使用 CRLF 表示 新的一行。

另外一些计算机系统,如 Unix 采用了别的做法。在计算机时代, 移到行首 + 指针往下移动一行的物理含义 已经不适用了,并且内存和软盘的空间又很贵,于是这些系统决定只使用一个字符来表示换行。如今的 Unix like 系统里大多采用 LF(\n),其中早期的 MacOS 使用 CR(\r)。

于是,有些系统延续了早期 CRLF 的做法,而有些系统使用 LF 的做法。

这就是标题中问题出现的根源。

代码编辑器

现在几乎所有的代码编辑器(包括 windows10 Notepad)都支持不同的换行模式,所以大多数时候无论源文件是用哪种方式表达换行的,编辑器都能帮我们正确显示。这就是我们在阅读代码时感觉不到这种差异的原因。

Git

阅读代码时,编辑器可以帮助我们选择合适的方式展示。但是涉及到源代码的存储和传输的时候,似乎我们只能选择一个事实标准,否则即使实际代码内容没有改变,依然可能会因为不同系统处理换行的方式不同,导致大量的 git diff。

对于跨平台的开发环境,git 也确实是这么推荐(搜索core.autocrlf)。git 使用 LF-style。

对于 Windows 来说:

  1. 当从远程拉取代码到本地时,git 把代码执行转换: LF → CRLF
  2. 当提交代码时,git 对代码 执行 CRLF → LF。因此 git diff 会在 LF-style 层面执行,不会因为行结束符产生大量的diff结果。

对于 Unix like 来说:

  • 整个开发流程都是 LF-style

如果对设置有疑问,可以参照 github suggestion 去操作。

实际上这个设置默认上是关闭的,windows git installer 会帮用户默认勾选打开这个选项。

2021-04-01_9.20.37.png

Prettier

事实上, 由于 Windows 上 vscode 等常用编辑器是完全支持 LF-style 的。我们团队现有做法是,checkout code 和 commit 时 使用 perttier,把代码全部转为 LF-style (prettier 2.0 版本后的默认格式是 LF)。这样,在 Windows 环境下,所有的代码也全程都是 LF-style 的。即使 windows 用户的 git config 设置错误,不会做 CRLF → LF 的转换,那么 CRLF 格式的代码也不会被提交到远程仓库,从而避免了更多的问题。

你相信光吗?

LF/CRLF 的现在带来的问题更多是由于历史原因造成的。或许我们可以期待有一天,各个平台会统一,就像Mac-OS 的换行已经从早期的 CR 转变成了现在与 Unix 统一的 LF。