我的可执行文件中有以下脚本test-shebang.mjs
,我想用它zx
来运行我的脚本,但~/.zshrc
在此之前要先获取我的脚本:
#!/usr/bin/env -S zsh -c 'source ~/.zshrc; zx --install $@' --
console.log("work pls")
./test-shebang.mjs
在 Ubuntu 中工作正常:
❯ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
❯ zsh --version
zsh 5.8.1 (x86_64-ubuntu-linux-gnu)
但是当我在 macOS 中复制相同的脚本时,出现以下错误:
❯ ./test-shebang.mjs
zsh:1: unmatched '
❯ zsh --version
zsh 5.9 (x86_64-apple-darwin23.0)
❯ sw_vers
ProductName: macOS
ProductVersion: 14.4
BuildVersion: 23E214
为什么会出现这种情况?
我也尝试过,bash
但也遇到了错误。 FWIW,我正在做这种迂回的做事方式,因为我已经zx
通过pnpm
它安装了它本身brew
,我不想设置PATH
它只会使脚本更长。
答案1
Shebang 行在不同 Unix 内核上的解析略有不同。在 Linux 和现代 BSD 上,该命令后跟一个参数(或无参数),该参数可以包含空格。在 macOS 上,该命令后跟零个或多个用空格分隔的参数。
因此,在 Linux 上,当您使用./test-shebang.mjs
参数运行时foo
,会发生以下情况:
- 内核
/usr/bin/env
使用参数运行-S zsh -c 'source ~/.zshrc; zx --install $@' --
(请注意,所有这些都是第一个参数),./test-shebang.mjs
,foo
。 env
zsh
使用参数-c
,source ~/.zshrc; zx --install $@
,--
,./test-shebang.mjs
,运行foo
。source ~/.zshrc; zx --install $@
Zsh使用脚本名称--
和两个位置参数./test-shebang.mjs
,运行脚本foo
。- 返回后
.zshrc
,zshzx
使用参数--install
,./test-shebang.mjs
,运行foo
。
在 macOS 上,第一步是不同的(继承自旧版本的 FreeBSD),这会导致在第三步中检测到意外的数据:
- 内核
/usr/bin/env
使用参数-S
,zsh
,-c
,'source
,~/.zshrc;
,zx
,--install
,$@'
,--
,./test-shebang.mjs
,运行foo
。 env
zsh
使用参数-c
,'source
,~/.zshrc;
,zx
,--install
,$@'
,--
,./test-shebang.mjs
,运行foo
。'source
Zsh使用脚本名称~/.zshrc;
和位置参数zx
,--install
,$@'
,--
,./test-shebang.mjs
,运行脚本foo
。- Zsh 抱怨这
'source
不是一个语法正确的脚本。
不幸的是,env -S
在 Linux 和 FreeBSD 上弥补了它们的 shebang 解析怪异,但 macOS 将 FreeBSD 的env
实用程序与不同的 shebang 解析混合在一起,因此env -S
在 macOS 上无法按预期工作(或以任何特别有用的方式)。
复杂 shebangs 的便携式解决方案是编写一个多语言带有#!/bin/sh
shebang,第二行包含专门编写的 shell 代码,无论脚本使用什么语言,都可以执行无操作。例如,这里有一个 sh+JavaScript 多语言(假设一个 JS 变体将 shebang 行视为注释)和你的 shebang 行做同样的事情。
#!/bin/sh
eval : '; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"';
console.log("Javascript code starts here");
eval
在 sh 中,第二行是带有两个参数:
和 的内置命令; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"
。这会导致 sh 运行: ; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"
。:
是一个无操作命令。内置exec
导致 sh 将自身替换为 zsh,因此 sh 在第二行之后停止解析文件。- 在 JavaScript 中,第二行是一个字符串文字(没有效果),带有标签
eval
(无用但无害)。
请注意,您在脚本中放入的内容确实很奇怪并且不太可能有帮助。.zshrc
用于交互式定制,在非交互式 shell 中使用时可能会导致奇怪的效果。.zshrc
不应设置环境变量,因为这些变量在非终端程序中不可用,并且如果运行嵌套的 zsh,这些变量将被重置;你应该设置环境变量.zprofile
。