shebang 줄에 인용 문자열이 포함된 env -S가 Ubuntu에서는 제대로 작동하지만 macOS에서는 작동하지 않는 이유는 무엇입니까?

shebang 줄에 인용 문자열이 포함된 env -S가 Ubuntu에서는 제대로 작동하지만 macOS에서는 작동하지 않는 이유는 무엇입니까?

내 실행 파일에 다음 스크립트가 있습니다 . 이를 사용하여 스크립트를 실행하고 test-shebang.mjs싶지만 그 전에 스크립트를 가져와야 합니다.zx~/.zshrc

#!/usr/bin/env -S zsh -c 'source ~/.zshrc; zx --install $@' --

console.log("work pls")

./test-shebang.mjs우분투에서는 잘 작동합니다:

❯ 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에서는 명령 뒤에 공백으로 구분된 0개 이상의 인수가 옵니다.

따라서 Linux에서 ./test-shebang.mjs매개 변수를 사용하여 실행 하면 foo다음이 발생합니다.

  • 커널은 /usr/bin/env인수 -S zsh -c 'source ~/.zshrc; zx --install $@' --(이들 모두가 첫 번째 인수라는 점에 유의하세요), ./test-shebang.mjs, 를 사용하여 실행됩니다 foo.
  • envzsh-c, source ~/.zshrc; zx --install $@, --, 매개변수로 ./test-shebang.mjs실행합니다 foo.
  • source ~/.zshrc; zx --install $@Zsh는 스크립트 이름 --과 두 개의 위치 매개변수를 사용하여 ./test-shebang.mjs스크립트를 실행합니다 foo.
  • 반환 후 .zshrczsh는 매개변수 , , zx로 실행됩니다 .--install./test-shebang.mjsfoo

macOS에서는 첫 번째 단계가 다르므로(이전 버전의 FreeBSD에서 상속됨) 세 번째 단계에서 예기치 않은 데이터가 감지됩니다.

  • 커널은 , , , , , , , , 매개 /usr/bin/env변수로 실행됩니다 .-Szsh-c'source~/.zshrc;zx--install$@'--./test-shebang.mjsfoo
  • envzsh-c, 'source, ~/.zshrc;, zx, --install, $@', --, 매개변수를 사용하여 ./test-shebang.mjs실행합니다 foo.
  • 'sourceZsh는 스크립트 이름 ~/.zshrc;과 위치 매개변수 zx, --install, $@', --, 를 사용하여 ./test-shebang.mjs스크립트를 실행합니다 foo.
  • Zsh는 이것이 'source구문상 올바른 스크립트가 아니라고 불평합니다.

불행하게도 env -SLinux와 FreeBSD에서는 Shebang 구문 분석의 이상한 점을 보완하지만 macOS는 envFreeBSD의 유틸리티를 다른 Shebang 구문 분석과 혼합하므로 env -SmacOS에서는 예상대로(또는 특히 유용한 방식으로) 작동하지 않습니다.

복잡한 shebang에 대한 이식 가능한 솔루션은 다음을 작성하는 것입니다.다국어shebang 의 #!/bin/sh두 번째 줄에는 스크립트에서 사용되는 언어에 관계없이 아무 작업도 수행하지 않는 특별히 작성된 셸 코드가 포함되어 있습니다. 예를 들어, 다음은 shebang 줄과 동일한 작업을 수행하는 sh+JavaScript 다중 언어(shebang 줄을 주석으로 처리하는 JS 변형을 가정)입니다.

#!/bin/sh
eval : '; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@"';
console.log("Javascript code starts here");
  • evalsh에서 두 번째 줄은 :두 개의 매개변수와 ; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@". 이로 인해 sh가 실행됩니다 : ; exec zsh -c "source ~/.zshrc; zx --install \$@" -- "$0" "$@". :작동하지 않는 명령입니다. 내장 기능으로 exec인해 sh는 자체적으로 zsh로 대체되므로 sh는 두 번째 줄 이후 파일 구문 분석을 중지합니다.
  • JavaScript에서 두 번째 줄은 문자열 리터럴(효과 없음)이며 레이블 eval(쓸모는 없지만 무해함)이 있습니다.

스크립트에 넣은 내용은 정말 이상하고 도움이 될 것 같지 않습니다. .zshrc대화형 사용자 정의에 사용되며, 비대화형 셸에서 사용하면 이상한 효과가 발생할 수 있습니다. .zshrc환경 변수는 비터미널 프로그램에서 사용할 수 없으며 중첩된 zsh를 실행하면 재설정되므로 환경 변수를 설정해서는 안 됩니다 .zprofile.

관련 정보