スクリプトの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のスクリプトサポートがあります。

3つすべてで同じ実行可能な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スクリプトを実行することができます。私は2つのファイルを配布するのが面倒であることがわかりました。最新のPython 2インタプリタがインストールされた状態で実行されている場合は、1つのファイルで十分です。人に通訳者を明示的に(たとえば$ 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)

これは尊敬される 渡す Python 文書すべてのバージョンで:

良い選択は一般的に

#!/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操作の例とさまざまな結果を見つけることができます。

脚注

* Gentooにはというツールがあります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バージョンを使用して解釈されます。

いくつかのいたずらの説明:

私が追加した3つの引用符を使用すると、同じスクリプトをPythonモジュール(テスト目的で使用)にインポートすることもできます。 Pythonにインポートすると、最初と2番目の3つの単一引用符の間のすべての内容がモジュールレベルの文字列として解釈され、3番目の3つの単一引用符がコメント化されます。残りはちょうど普通のPythonです。

直接実行すると(今すぐbashスクリプトで)、最初の2つの一重引用符は空の文字列になり、3番目の一重引用符はコロンのみを含む4番目の一重引用符で別の文字列を形成します。 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

すべてを1つのファイルに入れるには、通常、次のように作成できます。多言語1行で始まります#!/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文字列であり、効果はありません。シェルの場合、2行目は''':'引用符なしのno-opコマンドです:。)

答え4

使用可能なphython実行可能ファイルを確認し、スクリプトを引数として使用して呼び出す小さなbashスクリプトを作成できます。その後、このスクリプトをshebang行として指定できます。

#!/my/python/search/script

このスクリプトは検索後に次のことを行います。

"$python_path" "$1"

カーネルがこのスクリプト間接参照を許可するかどうかはわかりませんが、確認してみてください。

編集1

したがって、この恥ずかしい無知は結局良いアドバイスになります。

2つのスクリプトを1つのファイルに結合することが可能です。ここで説明されているように、bashスクリプトにpythonスクリプトを作成するだけです(pythonスクリプトを変更した場合は、スクリプトを再度一緒にコピーするだけです)。たとえば、/ tmpに一時ファイルを作成できます。または(Pythonがサポートしている場合はわかりません)、インタプリタに入力としてスクリプトを提供します。

# 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

関連情報