shebang의 여러 주장


32

내가 오두막 라인을 통해 실행에 여러 옵션을 전달하는 일반적인 방법이 있는지 궁금 ( #!).

나는 NixOS를 사용하며, 내가 쓰는 스크립트에서 shebang의 첫 번째 부분은 보통 /usr/bin/env입니다. 내가 겪는 문제는 시스템에 의해 이후의 모든 것이 단일 파일 또는 디렉토리로 해석된다는 것입니다.

예를 들어 bashposix 모드에서 실행할 스크립트를 작성한다고 가정 하십시오. shebang을 작성하는 순진한 방법은 다음과 같습니다.

#!/usr/bin/env bash --posix

그러나 결과 스크립트를 실행하려고하면 다음 오류가 발생합니다.

/usr/bin/env: ‘bash --posix’: No such file or directory

나는 이 게시물을 알고 있지만 더 일반적이고 깨끗한 해결책이 있는지 궁금합니다.


편집 : Guile 스크립트 의 경우 매뉴얼의 4.3.4 섹션에 설명 된대로 원하는 것을 얻을 수있는 방법이 있다는 것을 알고 있습니다.

 #!/usr/bin/env sh
 exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
 !#

여기서 트릭은 (로 시작하는 exec) 두 번째 줄 이 코드로 해석 sh되지만 #!... !#블록에 주석으로 표시되어 Guile 인터프리터에 의해 무시된다는 것입니다.

이 방법을 통역사에게 일반화 할 수 없습니까?


두 번째 편집 : 조금 놀아 본 후에서 입력을 읽을 수있는 통역사의 stdin경우 다음 방법이 작동 하는 것으로 보입니다 .

#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;

그러나 sh통역사가 작업을 마치기 전까지는 프로세스가 진행 되므로 최적이 아닐 수도 있습니다. 모든 의견이나 제안을 부탁드립니다.



답변:


27

Linux 커널은 shebang 행의 첫 번째 "단어"뒤에 나오는 모든 것을 단일 인수로 취급 하기 때문에 적어도 Linux를 지원해야하는 경우에는 일반적인 해결책이 없습니다 .

NixOS의 제약 조건이 무엇인지 잘 모르겠지만 일반적으로 다음과 같이 shebang을 작성합니다.

#!/bin/bash --posix

또는 가능한 경우 스크립트에서 옵션을 설정하십시오 .

set -o posix

또는 적절한 쉘 호출로 스크립트를 다시 시작하도록 할 수 있습니다.

#!/bin/sh -

if [ "$1" != "--really" ]; then exec bash --posix -- "$0" --really "$@"; fi

shift

# Processing continues

셸에서 해석되는 첫 두 줄을 대상 언어에서 무시할 수있는 방법을 찾으면이 방법을 다른 언어로 일반화 할 수 있습니다.

GNU coreutils' env는 버전 8.30 이후의 해결 방법을 제공합니다 . 자세한 내용 은 unode답변 을 참조하십시오. (데비안 10 이상, RHEL 8 이상, 우분투 19.04 이상에서 사용 가능합니다.)


18

정확하게 이식 가능하지는 않지만 coreutils 8.30부터 시작 하여 설명서에 따라 다음을 사용할 수 있습니다.

#!/usr/bin/env -S command arg1 arg2 ...

그래서 주어진 :

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

당신은 얻을 것이다 :

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

궁금한 경우 showargs:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

나중에 참조 할 수있는 정보를 제공하는 것이 좋습니다.
John McGehee

그 옵션은 FreeBSD의의에서 복사 된 env경우 -S2005 참조 추가되었습니다 lists.gnu.org/r/coreutils/2018-04/msg00011.html
스테판 Chazelas가

Fedora 29
Eric

@unode 일부 개선 사항 showargs: pastebin.com/q9m6xr8Hpastebin.com/gS8AQ5WA (한 줄짜리)
Eric

참고 : coreutils 8.31 기준 env으로 자체 포함 showargs: -v 옵션#!/usr/bin/env -vS --option1 --option2 ...
chocolateboy

9

POSIX 표준은 다음을 설명하는 데 매우 간결합니다 #!.

로부터 의 문서의 근거 섹션 exec()시스템 인터페이스의 가족 :

일부 역사적인 구현에서 쉘 스크립트를 처리하는 또 다른 방법은 파일의 처음 2 바이트를 문자열로 인식하고 파일 #!의 첫 번째 행의 나머지 부분을 실행할 명령 인터프리터의 이름으로 사용하는 것입니다.

로부터 쉘 소개 섹션 :

쉘은 파일 sh, -c옵션 또는 POSIX.1-2008의 시스템 인터페이스 볼륨에 정의 된 system()popen()기능 에서 입력을 읽습니다 . 쉘 명령 파일의 첫 번째 행이 문자로 시작 #!하면 결과가 지정되지 않습니다 .

이것은 기본적으로 모든 구현 (사용중인 유닉스)이 원하는대로 shebang 라인의 구문 분석에 대해 자유롭게 할 수 있음을 의미합니다.

macOS (ATM을 테스트 할 수 없음)와 같은 일부 Unices는 shebang 행의 인터프리터에 제공된 인수를 별도의 인수로 분할하지만 Linux 및 대부분의 다른 Unices는 인수를 단일 옵션으로 해석기에 제공합니다.

따라서 하나 이상의 논쟁을 취할 수있는 shebang 노선에 의존하는 것은 현명하지 않습니다.

위키 백과에 대한 Shebang 기사이식성 섹션 도 참조하십시오 .


유틸리티 나 언어로 일반화 할 수있는 쉬운 솔루션 중 하나는 적절한 명령 행 인수로 실제 스크립트를 실행하는 랩퍼 스크립트를 작성하는 것입니다.

#!/bin/sh
exec /bin/bash --posix /some/path/realscript "$@"

나는 개인적으로 다시 실행하기 위해 노력할 것이라고 생각하지 않는다 자체 가 다소 허약 한 느낌으로.


7

shebang은 execve(2) 매뉴얼 페이지에 다음과 같이 설명되어 있습니다 .

#! interpreter [optional-arg]

이 구문에는 두 개의 공백이 허용됩니다.

  1. 인터프리터 경로 앞의 한 이지만이 칸은 선택 사항입니다.
  2. 인터프리터 경로와 선택적 인수를 구분하는 하나의 공간 .

내가 사용하는 위의 구문을 선택적 인수로 이야기 할 때 복수를 사용하지, 어느 쪽도하지 않았다 것을 참고 [optional-arg ...]로, 대부분 하나 개의 인수로 제공 할 수 있습니다 .

쉘 스크립팅에 관한 한, set스크립트 시작 부분 근처에 내장 명령을 사용하면 인터프리터 매개 변수를 설정하여 명령 행 인수를 사용한 것과 동일한 결과를 제공 할 수 있습니다.

귀하의 경우 :

set -o posix

Bash 프롬프트에서 help set사용 가능한 모든 옵션을 얻으려면 출력을 확인하십시오 .


1
공백을 두 개 이상 가질 수 있으며 선택적 인수의 일부로 간주됩니다.
Stephen Kitt

@StephenKitt : 실제로 공백은 실제 공백 문자보다 범주로 더 많이 사용됩니다. 탭과 같은 다른 공백도 널리 허용되어야한다고 가정합니다.
WhiteWinterWolf

3

Linux에서 shebang은 매우 유연하지 않습니다. 여러 답변 ( Stephen Kitt의 답변Jörg W Mittag의 )에 따르면, 세방 라인에서 여러 인수를 전달하는 지정된 방법이 없습니다.

누구에게나 사용할 것인지 잘 모르겠지만 부족한 기능을 구현하기 위해 짧은 스크립트를 작성했습니다. https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa를 참조 하십시오 .

포함 된 해결 방법을 작성할 수도 있습니다. 벨로우, 나는 같은 테스트 스크립트에 적용되는 4 가지 언어 에 대한 해결책과 각각의 결과를 제시한다. 스크립트가 실행 가능하고에 있다고 가정합니다 /tmp/shebang.


프로세스 대체 내부의 bash heredoc에 스크립트 래핑

내가 아는 한, 이것이 가장 신뢰할 수있는 언어 독립적 방법입니다. 인수를 전달하고 stdin을 보존합니다. 단점은 인터프리터가 읽는 파일의 (실제) 위치를 모른다는 것입니다.

#!/bin/bash
exec python3 -O <(cat << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv
try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER
) "$@"

호출 echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'인쇄 :

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /dev/fd/62
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: False
PYTHON_SCRIPT_END

프로세스 대체는 특수 파일을 생성합니다. 모든 실행 파일에 적합한 것은 아닙니다. 예를 들어 다음과 같이 #!/usr/bin/less불평합니다./dev/fd/63 is not a regular file (use -f to see it)

heredoc을 프로세스 대체 내부에 대시로 넣을 수 있는지 모르겠습니다.


간단한 heredoc에서 스크립트 감싸기

더 짧고 간단하지만 stdin스크립트에서 액세스 할 수 없으며 인터프리터가에서 스크립트를 읽고 실행할 수 있어야합니다 stdin.

#!/bin/sh
exec python3 - "$@" << 'EOWRAPPER'
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

try:
    print("input() 0 ::", input())
    print("input() 1 ::", input())
except EOFError:
    print("input() caused EOFError")
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")
EOWRAPPER

호출 echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'인쇄 :

PYTHON_SCRIPT_BEGINNING
input() caused EOFError
argv[0]   :: -
argv[1:]  :: ['arg1', 'arg2 contains spaces', 'arg3\\ uses\\ \\\\escapes\\\\']
__debug__ :: True
PYTHON_SCRIPT_END

system()인수없이 awk 호출 사용

실행 된 파일의 이름을 올바르게 전달하지만 스크립트는 사용자가 제공 한 인수를받지 않습니다. awk는 인터프리터가 기본적으로 리눅스에 설치되어 있으며 명령 줄에서 기본적으로 명령을 읽는 유일한 언어입니다.

#!/usr/bin/gawk BEGIN {system("python3 -O " ARGV[1])}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

호출 echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'인쇄 :

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: []
__debug__ :: False
PYTHON_SCRIPT_END

system()인수에 공백이 없으면 awk 4.1 이상 호출을 사용하십시오.

훌륭하지만 공백이 포함 된 인수로 스크립트를 호출하지 않을 경우에만 가능합니다. 보시다시피 공백이 포함되지 않은 인수는 공백이 이스케이프되지 않는 한 분할됩니다.

#!/usr/bin/gawk @include "join"; BEGIN {system("python3 -O " join(ARGV, 1, ARGC, " "))}
print("PYTHON_SCRIPT_BEGINNING")

from sys import argv

print("input() 0 ::", input())
print("input() 1 ::", input())
print("argv[0]   ::", argv[0])
print("argv[1:]  ::", argv[1:])
print("__debug__ ::", __debug__)
# The -O option changes __debug__ to False

print("PYTHON_SCRIPT_END")

호출 echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'인쇄 :

PYTHON_SCRIPT_BEGINNING
input() 0 :: aa
input() 1 :: bb
argv[0]   :: /tmp/shebang
argv[1:]  :: ['arg1', 'arg2', 'contains', 'spaces', 'arg3 uses \\escapes\\']
__debug__ :: False
PYTHON_SCRIPT_END

4.1 미만의 awk 버전의 경우 for 루프 내에서 문자열 연결을 사용해야합니다 (예제 함수 https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html 참조) .


1
금지 $variable또는 `command`대체 할 문서 종결자를 인용하십시오 .exec python3 -O <(cat <<'EOWRAPPER'
John McGehee

2

쉘 이외의 다른 것에 의존하지 않고 취급 LD_LIBRARY_PATH하는 #!(shebang) 라인 에서 파이썬과 함께 사용하는 트릭 :

#!/bin/sh
'''' 2>/dev/null; exec /usr/bin/env LD_LIBRARY_PATH=. python -x "$0" "$@" #'''

__doc__ = 'A great module docstring'

이 페이지의 다른 곳에서 설명한 것처럼 일부 쉘 sh은 표준 입력에 스크립트를 사용할 수 있습니다.

우리가 줄 스크립트 sh명령 실행 시도 ''''로 단순화 ''하여 (빈 문자열) sh과 더 없습니다로 실행되지 물론 ''정상적으로 출력되도록 명령은 line 2: command not found표준 오류 설명에 그러나 우리는 사용이 메시지 리디렉션 2>/dev/null받는 사람을 가장 까다로운 블랙홀은 사용자가 sh표시 하기에 혼란스럽고 혼란 스러울 수 있습니다.

그런 다음 관심있는 명령으로 진행 exec합니다. 현재 쉘 프로세스를 다음과 같은 경우 /usr/bin/env python에 적절한 매개 변수로 바꿉니다 .

  • "$0" 파이썬에게 어떤 스크립트를 열고 해석해야하는지 알리고 설정 sys.argv[0]
  • "$@"파이썬을 sys.argv[1:]스크립트 명령 줄에 전달 된 인수 로 설정하십시오 .

또한 환경 변수 env를 설정 하도록 요청 합니다. LD_LIBRARY_PATH환경 변수는 해킹의 유일한 지점입니다.

쉘 명령은 주석으로 시작 #하여 쉘은 후행 삼중 따옴표를 무시합니다 '''.

sh그런 다음 첫 번째 인수 ( "$0") 로 주어진 파이썬 소스 스크립트를 열고 읽는 파이썬 인터프리터의 시니 새 인스턴스로 바뀝니다 .

파이썬은 파일을 열고 -x인수 덕분에 소스의 첫 번째 줄을 건너 뜁니다 . 참고 : -x파이썬의 경우 shebang이 단지 주석이기 때문에 작동하지 않습니다 .

그런 다음 파이썬은 두 번째 줄을 현재 모듈 파일의 docstring으로 해석하므로 유효한 모듈 docstring이 필요하면 __doc__위의 예와 같이 파이썬 프로그램에서 먼저 설정 하십시오.



빈 문자열이… 음… 비어 있다고 가정하면 원숭이 사업을 찾을 수없는 명령을 삭제할 수 있어야합니다 ''''exec .... 작업을 완료해야합니다. exec 앞에 공백이 없으면 빈 명령을 찾게됩니다. 당신은 너무 그래서 첫 번째 인수에 빈 스플 라이스 할 $0것입니다 exec.
갈렙

1

스크립트를 단일 인수로 제외한 실행 파일을 찾을 때 다소 어리석은 해결 방법을 찾았습니다.

#!/usr/bin/awk BEGIN{system("bash --posix "ARGV[1])}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.