스크립트의 shebang 줄에 어떻게 두 개 이상의 가능성이 있을 수 있나요?

스크립트의 shebang 줄에 어떻게 두 개 이상의 가능성이 있을 수 있나요?

내 상황은 약간 흥미롭습니다. 이론적으로 다양한 환경(및 경로) 및 다양한 Linux 시스템에서 다양한 사용자가 실행할 수 있는 Python 스크립트가 있습니다. 나는 이 스크립트가 인위적인 제한 없이 가능한 한 많은 스크립트에서 실행되기를 원합니다. 알려진 설정은 다음과 같습니다.

  • Python 2.6은 시스템 Python 버전이므로 python, python2 및 python2.6은 모두 /usr/bin에 존재하며 동일합니다.
  • 위에서 언급했듯이 Python 2.6은 시스템 Python 버전이지만 Python 2.7은 python2.7로 함께 설치됩니다.
  • Python 2.4는 시스템 Python 버전이며 내 스크립트는 이를 지원하지 않습니다. /usr/bin에는 python, python2 및 python2.4에 해당하는 항목과 python2.5에 대한 스크립트 지원이 있습니다.

세 가지 모두에서 동일한 실행 가능 Python 스크립트를 실행하고 싶습니다. 처음에 /usr/bin/python2.7(존재하는 경우)을 사용하려고 시도한 다음 /usr/bin/python2.6으로 폴백한 다음 /usr/bin/python2.5로 폴백하면 괜찮습니다. 그런 다음 이들 중 어느 것도 존재하지 않으면 오류가 발생합니다. 하지만 나는 최신 2.x를 사용하고 싶지 않습니다. 단, 올바른 인터프리터가 존재한다면 그 중 하나를 찾을 수 있어야 합니다.

내 첫 번째 생각은 shebang 줄을 다음과 같이 변경하는 것이 었습니다.

#!/usr/bin/python

도착하다

#!/usr/bin/python2.[5-7]

이것은 bash에서 잘 작동하기 때문입니다. 그러나 스크립트를 실행할 때 다음 오류가 발생합니다.

/usr/bin/python2.[5-7]: bad interpreter: No such file or directory

좋아, 그래서 나는 bash에서도 작동하는 다음을 시도했습니다.

#!/bin/bash -c /usr/bin/python2.[5-7]

그러나 이번에도 실패합니다.

/bin/bash: - : invalid option

글쎄, 분명히 올바른 인터프리터를 찾기 위해 별도의 쉘 스크립트를 작성하고 찾은 인터프리터를 사용하여 Python 스크립트를 실행할 수 있습니다. 두 개의 파일을 배포하는 것이 번거롭다는 것을 알았습니다. 최신 Python 2 인터프리터가 설치된 상태에서 실행된다면 하나의 파일이면 충분합니다. 사람들에게 통역사를 명시적으로(예: $ python2.5 script.py) 호출하도록 요청하는 것은 선택 사항이 아닙니다. 어떤 방식으로든 설정된 사용자 경로에 의존하는 것도 불가능합니다.

편집하다:

Python 스크립트의 버전 확인은아니요Python 2.6에 존재하는 (그리고 2.5에서도 사용할 수 있는 from __future__ import with_statement) "with" 문을 사용하고 있기 때문에 작동합니다. 이로 인해 사용자에게 친숙하지 않은 SyntaxError로 인해 스크립트가 즉시 실패하고 버전을 먼저 확인하고 적절한 오류를 내보낼 기회가 없습니다.

예:(2.6보다 낮은 Python 인터프리터로 시도해 보세요)

#!/usr/bin/env python

import sys

print "You'll never see this!"
sys.exit()

with open('/dev/null', 'w') as out:
    out.write('something')

답변1

나는 전문가는 아니지만 사용할 정확한 Python 버전을 지정해서는 안 되며 선택은 시스템/사용자에게 맡겨서는 안 된다고 생각합니다.

또한 스크립트에서 Python 경로를 하드코딩하는 대신 다음을 사용해야 합니다.

#!/usr/bin/env python

또는

#!/usr/bin/env python3 (or python2)

이것은존경받는 통과 파이썬 문서모든 버전에서:

좋은 선택은 일반적으로

#!/usr/bin/env python

Python 인터프리터의 전체 경로를 검색합니다. 그러나 일부 Unices에는 env 명령이 없을 수 있으므로 /usr/bin/python을 인터프리터 경로로 하드코드해야 할 수도 있습니다.

다른 배포판에서는 Python이 다른 위치에 설치될 수 있으므로 env에서 검색됩니다 PATH. 이는 모든 주요 Linux 배포판에서 사용할 수 있어야 하며, 제가 아는 한 FreeBSD에서도 사용할 수 있습니다.

배포판*에서 선택한 PATH의 Python 버전을 사용하여 스크립트를 실행해야 합니다.

스크립트가 2.4를 제외한 모든 Python 버전과 호환되는 경우 내부가 Python 2.4에서 실행되고 있는지 확인하고 일부 정보를 인쇄한 후 종료해야 합니다.

더 읽어보세요

  • 여기Python이 다른 시스템에 설치될 수 있는 위치의 예를 찾을 수 있습니다.
  • 여기를 사용하면 몇 가지 장점과 단점을 발견할 수 있습니다 env.
  • 여기PATH 작업의 예와 다양한 결과를 찾을 수 있습니다.

각주

*젠투에는 이라는 도구가 있습니다 eselect. 이를 사용하면 다양한 애플리케이션(Python 포함)의 기본 버전을 기본값으로 설정할 수 있습니다.

$ eselect python list
Available Python interpreters:
  [1]   python2.6
  [2]   python2.7 *
  [3]   python3.2
$ sudo eselect python set 1
$ eselect python list
Available Python interpreters:
  [1]   python2.6 *
  [2]   python2.7
  [3]   python3.2

답변2

일부 댓글의 몇 가지 아이디어를 바탕으로 정말 보기 흉하지만 작동하는 것처럼 보이는 해킹을 구성했습니다. 이 스크립트는 Python 스크립트를 래핑하고 Document Here를 통해 Python 인터프리터에 전달하는 bash 스크립트가 됩니다.

처음에는:

#!/bin/bash

''':'
vers=( /usr/bin/python2.[5-7] )
latest="${vers[$((${#vers[@]} - 1))]}"
if !(ls $latest &>/dev/null); then
    echo "ERROR: Python versions < 2.5 not supported"
    exit 1
fi
cat <<'# EOF' | exec $latest - "$@"
''' #'''

Python 코드는 여기에 있습니다. 그런 다음 마지막에:

# EOF

사용자가 스크립트를 실행하면 나머지 스크립트는 여기에 설명된 대로 2.5에서 2.7 사이의 최신 Python 버전을 사용하여 해석됩니다.

일부 장난에 대한 설명:

내가 추가한 세 개의 따옴표를 사용하면 동일한 스크립트를 Python 모듈(테스트 목적으로 사용)로 가져올 수도 있습니다. Python으로 가져올 때 첫 번째와 두 번째 세 개의 작은따옴표 사이의 모든 내용은 모듈 수준 문자열로 해석되고 세 번째 세 개의 작은따옴표는 주석 처리됩니다. 나머지는 그냥 평범한 Python이다.

직접 실행하면(이제 bash 스크립트로) 처음 두 개의 작은 따옴표는 빈 문자열이 되고, 세 번째 작은 따옴표는 콜론만 포함하는 네 번째 작은 따옴표와 함께 또 다른 문자열을 형성합니다. Bash는 이 문자열을 작동하지 않는 것으로 해석합니다. 그 밖의 모든 것은 /usr/bin에서 Python 바이너리를 일치시키고, 마지막 바이너리를 선택하고, exec를 실행하여 파일의 나머지 부분을 here 문서로 전달하는 Bash 구문입니다. 여기에 있는 문서는 Python 삼중 작은따옴표로 시작하며 해시/파운드/옥탑 기호만 포함합니다. 그러면 스크립트의 나머지 부분은 "#EOF"라는 줄이 문서를 종료할 때까지 정상적으로 해석됩니다.

이것은 나에게 특이한 것 같아서 누군가가 더 나은 해결책을 갖고 있기를 바랍니다.

답변3

Shebang 라인은 인터프리터에 대한 고정 경로만 지정할 수 있습니다. #!/usr/bin/env에 통역사를 찾는 요령이 있지만 PATH그게 전부입니다. 더 복잡해지려면 일부 래퍼 셸 코드를 작성해야 합니다.

가장 확실한 해결책은 래퍼 스크립트를 작성하는 것입니다. Python 스크립트를 호출 foo.real하고 래퍼 스크립트를 만듭니다 foo.

#!/bin/sh
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0.real" "$@"
else
  exec python "$0.real" "$@"
fi

모든 것을 하나의 파일에 넣으려면 일반적으로 다음과 같이 만들 수 있습니다.다국어한 줄로 시작하지만 #!/bin/sh(따라서 쉘에 의해 실행됨), 다른 언어에서도 유효한 스크립트입니다. 언어에 따라서는 다국어 사용이 불가능할 수도 있습니다( #!예를 들어 문법 오류가 발생하는 경우). Python에서는 그다지 어렵지 않습니다.

#!/bin/sh
''':'
if type python2 >/dev/null 2>/dev/null; then
  exec python2 "$0.real" "$@"
else
  exec python "$0.real" "$@"
fi
'''
# real Python script starts here
def …

'''(와 사이의 전체 텍스트는 '''최상위 Python 문자열이며 효과가 없습니다. 셸의 경우 두 번째 줄은 ''':'따옴표가 없는 no-op 명령입니다 :.)

답변4

사용 가능한 phython 실행 파일을 확인하고 스크립트를 인수로 사용하여 호출하는 작은 bash 스크립트를 작성할 수 있습니다. 그런 다음 이 스크립트를 shebang 줄로 지정할 수 있습니다.

#!/my/python/search/script

이 스크립트는 검색 후 다음을 수행합니다.

"$python_path" "$1"

커널이 이 스크립트 간접 참조를 허용할지 확실하지 않지만 확인해 보니 작동합니다.

편집 1

따라서 이 당황스러운 무지는 결국 좋은 조언이 됩니다.

두 개의 스크립트를 하나의 파일로 결합하는 것이 가능합니다. 여기에 설명된 대로 bash 스크립트에 python 스크립트를 작성하면 됩니다(python 스크립트를 변경하는 경우 스크립트를 다시 함께 복사하면 됩니다). 예를 들어 /tmp에 임시 파일을 만들 수 있습니다. 또는 (파이썬이 지원하는 경우 모르겠습니다) 인터프리터에 입력으로 스크립트를 제공합니다.

# do the search here and then
# either
cat >"tmpfile" <<"EOF" # quoting EOF is important so that bash leaves the python code alone
# here is the python script
EOF
"$python_path" "tmpfile"
# or
"$python_path" <<"EOF"
# here is the python script
EOF

관련 정보