web-dev-qa-db-ja.com

どうして人々は#!/ usr/bin/env python ShebangをPythonスクリプトの最初の行に書いているのですか?

その行がなくてもファイルは同じように動作するようです。

921
john garcias

いくつかのバージョンのPythonがインストールされている場合、/usr/bin/envは、使用されるインタプリタがあなたの環境の$PATHの最初のものであることを確認します。代わりの方法は、#!/usr/bin/pythonのようなものをハードコードすることです。それは問題ありませんが、それほど柔軟ではありません。

Unixでは、解釈されることを意図された executable ファイルは、最初の行の先頭に#!を持ち、その後にインタプリタ(そしてそれが必要とするかもしれないフラグ)を置くことによってどのインタプリタを使うかを示すことができます。

あなたが他のプラットフォームについて話しているのであれば、もちろん、この規則は適用されません(しかしその "Shebang line"は害を及ぼさず、そのスクリプトをUnixベースのプラットフォーム with にコピーすれば助けになります。 Linux、Macなどとして).

985
Alex Martelli

それは Shebang line と呼ばれます。 Wikipediaの記事に説明されているように

コンピューティングでは、Shebang(ハッシュバング、ハッシュサンプリング、ポンドバング、またはクランチバングとも呼ばれます)は文字 "#!"を表します。それらがテキストファイルの最初の行としてインタプリタディレクティブの最初の2文字であるとき。 Unixライクなオペレーティングシステムでは、プログラムローダーはこれら2文字の存在をファイルがスクリプトであることを示すものとして扱い、ファイルの最初の行の残りで指定されたインタプリタを使ってそのスクリプトを実行しようとします。

Unix FAQエントリ も参照のこと。

Shebang行が実行されるインタプリタを決定しないWindowsでも、Shebang行でそれらを指定することによってインタプリタにオプションを渡すことができます。一般的なShebangの行を1回限りのスクリプト(SOの質問に答えるときに書いたものなど)にしておくと便利なので、Windowsと ArchLinux の両方ですばやくテストできます。

envユーティリティ を使用すると、パス上でコマンドを呼び出すことができます。

最初の残りの引数は呼び出すプログラム名を指定します。環境変数PATHに従って検索されます。残りの引数はすべてそのプログラムへの引数として渡されます。

241
Sinan Ünür

他の答えを少し拡張して、これは/usr/bin/env Shebang行の不用意な使用によってあなたのコマンドラインスクリプトがどのように問題を起こすことができるかの小さな例です:

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

JsonモジュールはPython 2.5には存在しません。

この種の問題を防ぐための1つの方法は、ほとんどのPythonsに通常インストールされているバージョン付きのpythonコマンド名を使用することです。

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Python 2.xとPython 3.xを区別するだけの場合は、最近のPython 3リリースでもpython3という名前が付けられています。

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")
144
Ned Deily

Pythonスクリプトを実行するには、シェルに3つのことを伝える必要があります。

  1. ファイルがスクリプトであること
  2. スクリプトを実行したいインタープリタ
  3. インタプリタの道

Shebangの#!は(1)を達成しました。 #文字は多くのスクリプト言語のコメントマーカーであるため、Shebangは#で始まります。したがって、Shebang行の内容はインタプリタによって自動的に無視されます。

envコマンドは(2.)と(3.)を実行します。 「重厚さ」を引用すると

envコマンドの一般的な用途は、envが起動するように指示されたコマンドを$ PATHで検索するという事実を利用して、インタプリタを起動することです。 Shebang行では絶対パスを指定する必要があり、またさまざまなインタプリタ(Perl、bash、python)の場所は大きく異なる可能性があるため、使用するのが一般的です。

/ bin/Perl、/ usr/bin/Perl、/ usr/local/bin/Perl、/ usr/local/pkg/Perl、/ fileserver/usr/bin/Perl、または/のいずれであるかを推測する代わりに#!/usr/bin/env Perlユーザーのシステム上のhome/MrDaniel/usr/bin/Perl ...

一方、envはほとんどの場合/ usr/bin/envにあります。 (そうでない場合を除いて;いくつかのシステムは/ bin/envを使用するかもしれませんが、それはかなりまれな機会であり、Linux以外のシステムでのみ起こります。)

81
Rose Perrone

おそらくあなたの質問はこの意味です。

あなたが使いたいのなら:$python myscript.py

その行はまったく必要ありません。システムがpythonを呼び出してからpythonインタプリタがスクリプトを実行します。

しかし、あなたが使用するつもりなら:$./myscript.py

通常のプログラムやbashスクリプトのように直接呼び出すと、そのプログラムを実行するために使用するプログラムをシステムに指定するためにその行を書く必要があります(そしてchmod 755で実行可能にします)。

40
user3765197

技術的には、Pythonでは、これは単なるコメント行です。

この行は、pyスクリプトシェルから(コマンド行から)を実行した場合にのみ使用されます。これは "Shebang!"として知られています。 、そしてPythonスクリプトだけでなく、さまざまな状況で使用されます。

ここでは、シェルにPythonの特定のバージョンを起動するように指示します(ファイルの残りの部分を処理するため)。

39
mjv

これを行うための主な理由は、オペレーティング・システム環境全体スクリプトの移植をすることです。

Mingwの下例えば、Pythonスクリプトを使用します。

#!/c/python3k/python 

そして、GNU/Linuxディストリビューションの下で、それはどちらかであります:

#!/usr/local/bin/python 

または

#!/usr/bin/python

そして全て(OS/X)の最高の商用UNIXのSW/HWシステムの下で、それは次のようになります。

#!/Applications/MacPython 2.5/python

またはFreeBSD上で:

#!/usr/local/bin/python

しかし、すべてのこれらの違いは、使用してすべてにわたってスクリプトの移植を行うことができます。

#!/usr/bin/env python
35

Linuxカーネルのexecシステムコールはシェバン(#!)をネイティブに理解します

あなたがbashをするとき:

./something

linuxでは、これはパス./somethingexecシステムコールを呼び出します。

カーネルのこの行はexecに渡されたファイルで呼ばれます: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if((bprm-> buf [0]!= '#')||(bprm-> buf [1]!= '!'))

これはファイルの最初のバイトを読み込み、それらを#!と比較します。

そうであれば、その行の残りの部分はLinuxカーネルによって解析され、最初の引数としてパス/usr/bin/env pythonと現在のファイルを使用して別のexec呼び出しが行われます。

/usr/bin/env python /path/to/script.py

これは、コメント文字として#を使用するすべてのスクリプト言語で機能します。

そして、はい、あなたは無限ループを作ることができます:

printf '#!/a\n' | Sudo tee /a
Sudo chmod +x /a
/a

Bashはエラーを認識します。

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#!は人間が読める形式のものですが、必須ではありません。

ファイルが異なるバイトで始まっている場合、execシステムコールは異なるハンドラを使用します。もう1つの最も重要な組み込みハンドラはELF実行可能ファイル用です。 https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 これはバイト7f 45 4c 46をチェックします。 .ELFのために人間が読めることもあります。これはELFファイルを読み、それを正しくメモリに入れ、そしてそれで新しいプロセスを開始します。以下も参照してください。 カーネルはLinuxで実行されている実行可能バイナリファイルをどのように取得しますか。

最後に、binfmt_miscメカニズムを使ってあなた自身のShebangハンドラを追加することができます。たとえば、.jarファイルに カスタムハンドラを追加できます 。このメカニズムはファイル拡張子でハンドラをサポートします。別のアプリケーションは 透過的に異なるアーキテクチャの実行ファイルをQEMUで走らせることです

_ posix _ はshebangsを指定しているとは思わない: https://unix.stackexchange.com/a/346214/32558 、根拠のある節や形式では言及されているが "実行可能スクリプトがシステムでサポートされている場合は、何らかの問題が発生する可能性があります。 " macOSとFreeBSDもそれを実装しているようです。

PATH検索動機

おそらく、shebangsが存在する大きな動機の1つは、Linuxでは、次のようにPATHからコマンドを実行したいということです。

basename-of-command

の代わりに:

/full/path/to/basename-of-command

しかし、それでは、Shebangメカニズムがないと、Linuxはどのようにして各タイプのファイルを起動するのかを知ることができるでしょうか。

コマンドで拡張子をハードコーディングする:

 basename-of-command.py

またはすべてのインタプリタにPATH検索を実装する:

python basename-of-command

可能性はあるでしょう、しかしこれは私達が今までにコマンドを別の言語にリファクタリングすることにした場合すべてが壊れるという大きな問題を抱えています。

Shebangsはこの問題を美しく解決します。

最も逃したことの1つを強調するのはおそらく理にかなっています、それは即時の理解を妨げるかもしれません。端末にpythonと入力すると、通常はフルパスを指定しません。代わりに、実行ファイルはPATH環境変数で検索されます。逆に、Pythonプログラム/path/to/app.pyを直接実行したいときは、どのインタプリタを使うべきかをシェルに伝えなければなりません(hashbangを通して、他の貢献者は上で説明しています)。

Hashbangはインタプリタへのフルパス を期待します。したがって、Pythonプログラムを直接実行するには、特にvirtualenvの使用を考慮すると、大きく異なるPythonバイナリへのフルパスを指定する必要があります。移植性に対処するために、/usr/bin/envのトリックが使用されています。後者はもともと環境をその場で変更し、その中でコマンドを実行することを目的としています。変更がない場合は、現在の環境でコマンドを実行します。これにより、効果的に同じPATHルックアップが実行されます。

unix stackexchangeからのソース

21
saaj

これは推奨される方法です。ドキュメントで提案されています。

2.2.2。実行可能なPythonスクリプト

BSD系のUnixシステムでは、シェルスクリプトのようにPythonスクリプトを直接実行可能にすることができます。

#! /usr/bin/env python3.2

http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts から

12

これは、どのプログラムがスクリプトを実行できるかをシェルに指示するシェル規約です。

#!/ usr/bin/env python

pythonバイナリへのパスに解決されます。

10
Frank Krueger

あなたはvirtualenvを使ってこの問題を試すことができます

これがtest.pyです。

#! /usr/bin/env python
import sys
print(sys.version)

仮想環境を作成する

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

各環境をアクティブにしてから違いを確認する

echo $PATH
./test.py
9
Sercan

その行がなくてもファイルは同じように動作するようです。

もしそうなら、おそらくあなたはWindows上でPythonプログラムを実行している? Windowsはその行を使用しません - 代わりに、ファイル拡張子に関連付けられたプログラムを実行するためにファイル名拡張子を使用します。

しかし 2011年に、 "Pythonランチャー" が開発されました。これは(ある程度)WindowsでのLinuxの動作を模倣しています。これは、どのPythonインタプリタを実行するかを選択することだけに限定されます。両方がインストールされているシステムでPython 2とPython 3のどちらかを選択します。ランチャーはオプションでPythonのインストールによってpy.exeとしてインストールされ、ランチャーがその行をチェックして指定されたPythonインタプリタバージョンを起動するように.pyファイルに関連付けることができます。

8
Craig McQueen

これは、「実際の」答えよりも多くの履歴情報として意味されます。

当時、デザイナーはすべてのものを置く場所について独自の概念を持っていて、時にはPython、Perl、Bash、または他の多くのGNU /オープンソースのものat all

これは、さまざまなLinuxディストリビューションにも当てはまりました。 Linuxでは、-FHS [1]より前に、/ usr/bin /または/ usr/local/bin /にpythonが含まれている場合があります。または、インストールされていない可能性があるため、独自にビルドして〜/ binに配置します

Solarisは、バークレーUnixからSystem Vへの移行の一部として、私が今まで取り組んだ中で最悪でした。一部のreally長いパスの場合。 Sunfreeware.comの各パッケージを独自のディレクトリにインストールしたことを思い出しましたが、バイナリが/ usr/binにシンボリックリンクされたかどうかは思い出せません。

ああ、時には/ usr/binがNFSサーバーにありました[2]。

そのため、envユーティリティはこれを回避するために開発されました。

次に、#!/bin/env interpreterを記述し、パスが適切である限り、合理的な実行の機会がありました。もちろん、reasonableは(PythonおよびPerlの場合)適切な環境変数も設定したことを意味します。 bash/ksh/zshの場合、うまくいきました。

人々がシェルスクリプト(Perlやpythonなど)を渡していたため、これは重要でした。RedHat Linuxワークステーションで/ usr/bin/pythonをハードコーディングすると、SGIで問題が発生します。 、IRIXはpythonを正しい場所に置いたと思います。しかし、Sparcステーションではまったく実行されない場合があります。

Sparcステーションが恋しいです。しかし、多くはありません。さて、E-Bayでトローリングしました。バステージ。

[1]ファイルシステム階層標準。 https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2]はい、そして時々人々はまだそのようなことをします。いや、カブORのどちらかをベルトにつけていませんでした。

6
Petro

どのインタプリタを使いたいのかを指定するだけです。これを理解するには、touch test.pyを実行して端末からファイルを作成し、そのファイルに次のように入力します。

#!/usr/bin/env python3
print "test"

スクリプトを実行可能にするためにchmod +x test.pyを実行します。この後./test.pyを実行すると、次のようなエラーが表示されます。

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

python3はprint演算子をサポートしていないためです。

それでは先に進み、コードの最初の行を次のように変更してください。

#!/usr/bin/env python2

python2はprint演算子をサポートしているので、testをstdoutに出力してもうまくいきます。スクリプトインタプリタを切り替える方法を学びました。

5
Pavel

スクリプトを仮想環境で実行している場合、たとえばvenvvenvを操作しながらwhich pythonを実行すると、Pythonインタプリタへのパスが表示されます。

~/Envs/venv/bin/python

仮想環境の 名はPythonインタプリタへのパスに が埋め込まれていることに注意してください。したがって、スクリプト内でこのパスをハードコーディングすると、2つの問題が発生します。

  • スクリプトをリポジトリにアップロードすると、 になり、他のユーザーは同じ仮想環境名 を持つことになります。これは彼らが最初に問題を特定した場合です。
  • あなたが 他の仮想環境で必要なパッケージをすべて持っていたとしても、複数の仮想環境でスクリプトを実行することはできません

したがって、 Jonathan の答えに付け加えると、理想的なShebangは#!/usr/bin/env pythonです。OS間の移植性だけでなく、仮想環境間の移植性のためにも!

5
Bruce Wayne

python2python3の間の移植性の問題を考慮すると、あなたのプログラムが両方と互換性がある場合を除き、常にどちらかのバージョンを指定するべきです。

一部のディストリビューションはしばらくの間python3にシンボリックリンクされたpythonを出荷しています - pythonpython2であることに頼らないでください。

これは PEP 394 によって強調されています。

プラットフォーム間の違いを許容するために、Pythonインタプリタを呼び出す必要があるすべての新しいコードは、pythonを指定するべきではなく、むしろpython2またはpython3(またはより具体的なpython2.xおよびpython3.xバージョン; 移行を参照)を指定するべきです。ノート )。この区別は、シェルスクリプトから呼び出すとき、system()呼び出しを介して呼び出すとき、または他のコンテキストで呼び出すときに行う必要があります。

3
Zulan

それはあなたがpythonの複数のバージョンを持っているときにどのバージョンのpythonを使ってプログラムを実行するかをインタプリタに伝えます。

2
Katie T