Eu tenho o seguinte script em um arquivo executável test-shebang.mjs
e queria usá-lo zx
para executar meu script, mas preciso que ele ~/.zshrc
seja originado antes disso:
#!/usr/bin/env -S zsh -c 'source ~/.zshrc; zx --install $@' --
console.log("work pls")
./test-shebang.mjs
funciona bem no 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)
Mas quando copio o mesmo script no macOS, recebo este erro:
❯ ./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
Por que isso acontece?
Eu tentei bash
também, mas também encontrei erros. FWIW, estou fazendo essa maneira indireta de fazer as coisas porque instalei zx
através pnpm
do qual ele próprio é instalado brew
e prefiro não definir PATH
o que apenas tornaria o script mais longo.
As linhas Shebang são analisadas de maneira ligeiramente diferente em diferentes kernels Unix . No Linux e no BSD moderno, o comando é seguido por um único argumento (ou nenhum), que pode conter espaços. No macOS, o comando é seguido por zero ou mais argumentos separados por espaços.
Então no Linux, quando você executa
./test-shebang.mjs
o argumentofoo
, é isso que acontece:/usr/bin/env
com os argumentos-S zsh -c 'source ~/.zshrc; zx --install $@' --
(observe que tudo isso é o primeiro argumento),./test-shebang.mjs
,foo
.env
é executadozsh
com os argumentos-c
,source ~/.zshrc; zx --install $@
,--
,./test-shebang.mjs
,foo
.source ~/.zshrc; zx --install $@
com o nome do script e--
os dois parâmetros posicionais./test-shebang.mjs
.foo
.zshrc
o retorno, zsh é executadozx
com os argumentos--install
,./test-shebang.mjs
,foo
.No macOS o primeiro passo é diferente (herdado de versões antigas do FreeBSD), o que resulta em dados não intencionais detectados no terceiro passo:
/usr/bin/env
com os argumentos-S
,zsh
,-c
,'source
,~/.zshrc;
,zx
,--install
,$@'
,--
,./test-shebang.mjs
,foo
.env
é executadozsh
com os argumentos-c
,'source
,~/.zshrc;
,zx
,--install
,$@'
,--
,./test-shebang.mjs
,foo
.'source
com o nome do script~/.zshrc;
e os parâmetros posicionaiszx
,--install
,$@'
,--
,./test-shebang.mjs
,foo
.'source
não é um script sintaticamente correto.Infelizmente,
env -S
no Linux e no FreeBSD compensa sua estranheza na análise shebang, mas o macOS misturaenv
o utilitário do FreeBSD com uma análise shebang diferente, portantoenv -S
não funciona como pretendido (ou de qualquer maneira particularmente útil) no macOS.A solução portátil para shebangs complexos é escrever um poliglota com um
#!/bin/sh
shebang e uma segunda linha que contém código shell escrito especialmente para ser autônomo em qualquer idioma em que o script esteja. Por exemplo, aqui está um poliglota sh + JavaScript (assumindo uma variante JS que trata linhas shebang como comentários) que faz a mesma coisa que sua linha shebang.eval
com os dois argumentos:
e; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"
. Isso faz com que sh seja executado: ; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"
.:
é um comando autônomo. Oexec
built-in faz com que sh se substitua por zsh, então sh para de analisar o arquivo após a segunda linha.eval
(que é inútil, mas inofensivo).Observe que o que você colocou no script é realmente estranho e provavelmente não será útil.
.zshrc
destina-se a personalizações interativas e provavelmente causará efeitos estranhos quando usado em um shell não interativo..zshrc
não deve definir variáveis de ambiente, pois essas variáveis não estariam disponíveis em programas não terminais e seriam redefinidas se você executasse um zsh aninhado; você deve definir variáveis de ambiente.zprofile
.