스크립트의 쉐방 라인에서 어떻게 하나 이상의 가능성을 가질 수 있습니까? 상황에 처해

나는 다양한 환경 (및 PATH)과 다양한 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가 있습니다.

이 세 가지 모두에서 동일한 실행 가능한 파이썬 스크립트를 실행하고 싶습니다. /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 2 인터프리터가 설치된 경우 하나만 있으면 두 파일을 배포하는 번거 로움이 있습니다. 사람들에게 통역사를 명시 적으로 호출하도록 요청 $ python2.5 script.py하는 것은 옵션이 아닙니다. 특정 방식으로 설정되는 사용자의 PATH에 의존하는 것도 옵션이 아닙니다.

편집하다:

Python 2.6에서 존재하는 “with”문을 사용하고 있기 때문에 Python 스크립트 내에서 버전 확인 기능 이 작동 하지 않습니다 (2.5에서와 함께 사용할 수 있음 from __future__ import with_statement). 이로 인해 스크립트가 사용자에게 친숙하지 않은 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')



답변

나는 전문가가 아니지만 정확한 파이썬 버전을 지정하여 사용하지 말고 그 선택을 시스템 / 사용자에게 맡겨서는 안된다고 생각합니다.

또한 스크립트에서 파이썬의 하드 코딩 경로 대신 이것을 사용해야합니다.

#!/usr/bin/env python

또는

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

모든 버전에서 Python doc권장 합니다 .

좋은 선택은 일반적으로

#!/usr/bin/env python

전체 PATH에서 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


답변

몇 가지 의견에서 얻은 아이디어를 바탕으로, 나는 작동하는 것처럼 보이는 추악한 해킹을 함께 모을 수있었습니다. 이 스크립트는 bash 스크립트가되어 Python 스크립트를 랩핑하여 “here document”를 통해 Python 인터프리터로 전달합니다.

처음에 :

#!/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 - "$@"
''' #'''

파이썬 코드가 여기에 있습니다. 그런 다음 맨 끝에 :

# EOF

사용자가 스크립트를 실행할 때 2.5에서 2.7 사이의 최신 Python 버전이 나머지 문서를 여기 문서로 해석하는 데 사용됩니다.

일부 shenanigans에 대한 설명 :

내가 추가 한 삼중 인용 항목도 동일한 스크립트를 Python 모듈 (테스트 목적으로 사용)로 가져올 수 있습니다. 파이썬에 의해 임포트 될 때, 첫 번째와 두 개의 삼중 인용 부호 사이의 모든 것은 모듈 레벨 문자열로 해석되고 세 번째 삼중 인용 부호는 주석 처리됩니다. 나머지는 일반적인 파이썬입니다.

bash 스크립트로 직접 실행하면 처음 두 개의 작은 따옴표가 빈 문자열이되고 세 번째 작은 따옴표는 콜론 만 포함하는 네 번째 작은 따옴표로 다른 문자열을 형성합니다. 이 문자열은 Bash에 의해 no-op로 해석됩니다. 다른 모든 것은 / usr / bin에서 Python 바이너리를 가져 와서 마지막 바이너리를 선택하고 exec를 실행하여 나머지 파일을 here 문서로 전달하는 Bash 구문입니다. 이 문서는 해시 / 파운드 / 옥토 프 기호 만 포함 된 Python 삼중 따옴표로 시작합니다. 그런 다음 나머지 스크립트는 ‘# EOF’를 읽는 행이 여기 문서를 종료 할 때까지 정상으로 해석됩니다.

나는 이것이 이상하다고 생각하므로 누군가 더 나은 해결책을 갖기를 바랍니다.


답변

shebang 행은 인터프리터에 대한 고정 경로 만 지정할 수 있습니다. 거기의 #!/usr/bin/env에서 통역을 찾기 위해 트릭 PATH하지만 그것 뿐이다. 더 정교하고 싶다면 래퍼 쉘 코드를 작성해야합니다.

가장 확실한 해결책은 랩퍼 스크립트를 작성하는 것입니다. 파이썬 스크립트를 호출하고 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있지만 다른 언어에서는 유효한 스크립트이기도합니다. 언어에 따라 폴리 글롯이 불가능할 수 있습니다 ( #!예 : 구문 오류가 발생하는 경우 ). 파이썬에서는 그리 어렵지 않습니다.

#!/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 …

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


답변

요구 사항에 알려진 바이너리 목록이 명시되어 있으므로 Python을 사용하여 다음을 수행 할 수 있습니다. 한 자릿수 마이너 / 주 버전의 파이썬에서는 작동하지 않지만 조만간 그 일이 일어나지 않습니다.

바이너리에 태그가 지정된 버전이 현재 실행중인 python 버전보다 높은 경우 순서가 지정된 버전의 파이썬 목록에서 디스크에있는 최상위 버전을 실행합니다. “순서가 높은 버전 목록”은이 코드에서 중요한 부분입니다.

#!/usr/bin/env python
import os, sys

pythons = [ '/usr/bin/python2.3','/usr/bin/python2.4', '/usr/bin/python2.5', '/usr/bin/python2.6', '/usr/bin/python2.7' ]
py = list(filter( os.path.isfile, pythons ))
if py:
  py = py.pop()
  thepy = int( py[-3:-2] + py[-1:] )
  mypy  = int( ''.join( map(str, sys.version_info[0:2]) ) )
  if thepy > mypy:
    print("moving versions to "+py)
    args = sys.argv
    args.insert( 0, sys.argv[0] )
    os.execv( py, args )

print("do normal stuff")

까다로운 파이썬에 대한 사과


답변

사용 가능한 phython 실행 파일을 확인하고 스크립트를 매개 변수로 사용하여 작은 bash 스크립트를 작성할 수 있습니다. 그런 다음이 스크립트를 shebang 라인 대상으로 만들 수 있습니다.

#!/my/python/search/script

그리고이 스크립트는 단순히 (검색 후) 수행합니다.

"$python_path" "$1"

커널이이 스크립트를 간접적으로 받아 들일지 확신 할 수 없었지만 확인하고 작동합니다.

편집 1

이 난처한 인식을 좋은 제안으로 만들려면 :

두 스크립트를 하나의 파일로 결합 할 수 있습니다. bash 스크립트에서 python 스크립트를 here 문서로 작성하기 만하면됩니다 (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


답변