Docker 里的 shebang 踩坑记
最近手头有一个 Node 后端项目需要用 Docker 来部署,于是火急火燎地开始学习 Docker ,但在写完 Dockerfile 后却出现了以下错误:
1 | env: can't execute 'node': No such file or directory |
由于我的项目需要依赖本地的 pandoc 环境,因此 Dockerfile 中需要先安装 pandoc。我采用了下载预编译的二进制文件后拷贝到 /usr/bin
的方法进行安装,相关配置如下:
1 | FROM node:16-alpine as node |
我初步判断是 pandoc 在执行 JS Filter 的时候没有检测到 Node 环境的存在,但奇了怪了,我的基础镜像明明就是 Node 镜像,为什么会识别不到呢?
认识 shebang
经过了长久的排查(甚至从最开始的官方 pandoc 镜像换成了手动 curl 安装),我确认了不是环境中没有 node
的问题,node
版本和环境变量都一切正常。
最终,我定位到了我的 filter.js
文件上。
1 |
|
这是一个简单的用 JS 写的 pandoc filter,功能是读取文档中的图片并转为 base64 格式,在我的 Windows 环境中运行起来没有任何问题,但是在 Docker 中运行就出现了问题,而问题就出在第一行上。
1 | !/usr/bin/env node |
第一行这个以 #!
开头的东西叫做 shebang
(又称 hashbang
),用于指定脚本文件的解释器。
在文件中存在 Shebang 的情况下,类 Unix 操作系统的程序加载器会分析 Shebang 后的内容,将这些内容作为解释器指令,并调用该指令,并将载有Shebang的文件路径作为该解释器的参数。
而 /usr/bin/env
是一个程序,用于在环境变量(PATH)中查找后面的解释器,作用是方便脚本在不同机器上可以正常运行。因此上面的 shebang
的意思就是:在 PATH
中寻找 node
来解释下面的脚本。
而报错信息:env: can't execute 'node': No such file or directory
实际上是 /usr/bin/env
发出的,表示不能在环境变量中找到 node
。
奇也怪哉,我在 Shell 里运行 node
明明好好的,说明环境变量没有问题,为什么会找不到呢?
CRLF 和 LF
提到 CRLF(Windows EOL) 和 LF(Linux EOL)大家一定都不陌生,因为一些历史原因,Windows(MS-DOS)和 Unix 系统的各种标准一直都有着某些刻意为之的区别,其中就包括 EOL(End-Of-Line,行尾符)。
- 在 Windows 中,行尾用
\r\n
两个字符来表示 - 在 Linux 中,行尾用
\n
一个字符表示 - 在 macOS 中,目前主流是用
\n
(LF)来表示
此前 CRLF 和 LF 一直没有给我造成太大的困扰,最多也就在 Git 提交时发出一些提示,因此我也没有太在意。
CRLF 和 shebang 的奇妙化学反应
现在我们在 Windows 下编辑一个 hello.js
脚本文件,目的就是打印 Hello World。
1 |
|
现在我们想让它在 Linux 中直接作为脚本运行,因此上传到了我的 WSL(Ubuntu)中,修改权限后运行:
可以看到这里出现了相似的错误 /usr/bin/env: ‘node\r’: No such file or directory
,因为后面有 \r
导致无法正常识别 node,连错误码都是一样的 127,基本可以锁定就是 \r
的问题了。
这里由于我使用的是 zsh + Windows Terminal,所以可以正常地打印出错误信息里的
\r
,如果是 Docker 中默认的 Bash 默认是打印不出\r
的,这也是我找了这么久才找出问题根源的原因 QaQ。
如何解决 CRLF 的问题
在 VS Code 下方底栏右侧可以看到当前行尾,如下图所示:
在 Windows 侧管理
首先,在 VS Code (或其他编辑器)中设置 File > EOL 为 \n
(LF),保证本地创建的文件都是LF的,如下图所示:
然后再配置 Git 相关配置,与之相关的配置有两项:
core.autocrlf
控制文件在 Windows 电脑上时是否自动转换 CRLF(Git 内部是 LF )true
提交时转换为 LF,检出时转换为 CRLFinput
提交时转换为 LF,检出时不进行转换false
提交和检出时都不进行传唤
core.safecrlf
控制文件中不能同时出现 CRLF 和 LFtrue
拒绝提交包含混合换行符的文件false
允许提交混合换行符的文件warn
提交混合换行符文件时发出警告
全局设置
如果你想所有代码文件都使用 LF 的话(需要与 Unix 合作的话推荐这样做)可以修改 Git 全局配置如下,配合编辑器让 Windows 处理 LF 行尾。
1 | git config --global core.safecrlf true # 不允许混合换行符提交 |
设置 .gitattributes
使用 .gitattributes
的好处是可以保证使用这个仓库的每一个人都有相同的 Git 配置,避免协作问题。
以下是 Github 官方的一个模板文件:
1 | # Set the default behavior, in case people don't have core.autocrlf set. |
修改第一行 * text=auto
为 * text=auto eol=lf
即可将所有代码文件的行尾都规定为 LF,如果不符合规范就不能提交。
在 Linux 侧管理
由于 CRLF 和 LF 其实是一个历史悠久的问题,从 DOS 时期就有了,所以 Linux 上也有不少转化工具,如 dos2unix
。
以上面的 hello.js
为例执行转换:
1 | sudo apt-get install dos2unix |
转化后就可以正确执行,运行结果如下:
- 标题: Docker 里的 shebang 踩坑记
- 作者: ChlorineC
- 创建于 : 2023-07-13 12:21:42
- 更新于 : 2024-10-18 18:03:42
- 链接: https://chlorinec.top/2023/07/13/Development/shell-shebang/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。