「#!/ path / to / NAME」の代わりに「#!/ usr / bin / env NAME」をシバンとして使用する方が良いのはなぜですか?


459

#!/path/to/NAME他の人(同じツールNAMEを使用)がshebang を持っている間に、他の人から取得したスクリプトの中にはshebang を持っていることに気付き#!/usr/bin/env NAMEます。

両方とも正常に動作するようです。チュートリアル(Pythonなど)では、後者の方が優れているという提案があるようです。しかし、なぜそうなのか、私にはよくわかりません。

後者のシバンを使用するには、NAMEがPATHに含まれている必要がありますが、最初のシバンにはこの制限がありません。

また、(私には)NAMEの場所を正確に指定するため、最初の方がより良いシバンになるようです。そのため、この場合、NAMEのバージョンが複数ある場合(たとえば、/ usr / bin / NAME、/ usr / local / bin / NAME)、最初のケースはどちらを使用するかを指定します。

私の質問は、なぜ最初のシバンが2番目のシバンよりも好まれるのかということです。


12
この回答を参照してください...
jasonwryan

@ TheGeeko61:私の場合、何かが壊れていて、いくつかの変数がenvにありませんでした。したがって、このシェバンを使用して、envが正しくロードされているかどうかを確認することをお勧めします。
ギガメグ

回答:


462

必ずしも良いとは限りません。

の利点は、ユーザー#!/usr/bin/env pythonのにpython最初に現れる実行可能ファイルを使用すること$PATHです。

欠点は、ユーザー#!/usr/bin/env pythonのにpython最初に現れる実行可能ファイルを使用すること$PATHです。

つまり、スクリプトの実行者によってスクリプトの動作が異なる可能性があります。1人のユーザーについて/usr/bin/pythonは、OSと共にインストールされたものを使用する場合があります。もう1つは、/home/phred/bin/python正しく動作しない実験的なものを使用する場合があります。

場合とpythonだけでインストールされている/usr/local/bin、持っていないユーザー/usr/local/binでは$PATHさえ、スクリプトを実行することはできません。(これはおそらく現代のシステムではあまり起こりそうにありませんが、より曖昧なインタプリタでは簡単に起こります。)

指定する#!/usr/bin/pythonことにより、特定のシステムでスクリプトを実行するために使用するインタープリターを正確に指定します。

もう1つの潜在的な問題は、#!/usr/bin/envトリック(暗黙的に渡されるスクリプトの名前以外の)引数をインタープリターに渡させないことです通常、これ問題ではありませんが、問題になる可能性があります。多くのPerlスクリプトはで記述されて#!/usr/bin/perl -wいますuse warnings;が、最近では推奨される代替品です。Cshスクリプトはを使用する必要があります#!/bin/csh -fが、そもそも cshスクリプトは推奨さません。しかし、他の例もあり得ます。

新しいシステムにアカウントを設定するときにインストールする個人用ソース管理システムには、いくつかのPerlスクリプトがあります。私は#!それをインストールするときに各スクリプトの行を変更するインストーラースクリプトを使用します$HOME/bin。(私は#!/usr/bin/perl最近以外のものを使用する必要はありませんでした;それはPerlがしばしばデフォルトでインストールされなかった時代に戻ります。)

些細な点:#!/usr/bin/envトリックは間違いなくenvコマンドの悪用です。これは元々(名前が示すように)変更された環境でコマンドを呼び出すことを目的としていました。さらに、一部の古いシステム(正しく思い出せばSunOS 4を含む)にはのenvコマンドがありませんでした/usr/bin。これらのどちらも重大な懸念事項ではないでしょう。 envこのように機能し、多くのスクリプトがこの#!/usr/bin/envトリックを使用します。OSプロバイダーはそれを破るために何もしません。それは可能性がある、あなたのスクリプトは本当に古いシステム上で実行したい場合に問題となるが、その後、あなたはとにかくそれを修正する必要がありそうです。

別の考えられる問題(コメントで指摘してくれたSopalajo de Arrierezに感謝)は、cronジョブが制限された環境で実行されることです。特に、$PATH通常はのようなものです/usr/bin:/bin。その$PATHため、ユーザーシェルのデフォルトにある場合でも、インタープリターを含むディレクトリがこれらのディレクトリの1つにない場合、/usr/bin/envトリックは機能しません。正確なパスを指定するか、crontabに行を追加して設定できます$PATHman 5 crontab詳細について)。


4
/ usr / bin / perlがperl 5.8で、$ HOME / bin / perlが5.12であり、5.12を必要とするスクリプトがシェバンに/ usr / bin / perlをハードコードしている場合、スクリプトを実行するのは大きな苦痛です。PATHから/ usr / bin / env perl grab perlが問題になることはめったにありませんが、非常に役立つことがよくあります。そして、それはexecハックよりもずっときれいです!
ウィリアムパーセル

8
特定のインタープリターバージョンを使用する場合は、/ usr / bin / envの方が優れていることに注意してください。通常、perl5、perl5.12、perl5.10、python3.3、python3.32などの名前のマシンに複数のインタープリターバージョンがインストールされているため、アプリがその特定のバージョンでのみテストされている場合でも、指定できます#!/ usr / bin / env perl5.12そして、ユーザーが異常な場所にインストールした場合でも大丈夫です。私の経験では、「python」は通常、システムの標準バージョンへの単なるシンボリックリンクです(必ずしもシステム上の最新バージョンではありません)。
ルート

6
@root:Perlの場合、その目的の一部use v5.12;果たします。また、システムにPerl 5.14があり、5.12がない場合は失敗します。Python 2と3の場合、動作する可能性があります。#!/usr/bin/env perl5.12#!/usr/bin/python2#!/usr/bin/python3
キーストンプソン

3
@GoodPerson:にインストールするPerlスクリプトを作成するとし/usr/local/binます。で正しく動作することを知りました/usr/bin/perl。私は持っていないない考え、それが何で動作するかどうかをperlいくつかのランダムなユーザが自分の中に持っていることを起こる実行可能ファイルを$PATH。たぶん誰かがPerlの古いバージョンを試しているかもしれません。を指定した#!/usr/bin/perlので、スクリプト(ユーザーは必ずしもPerlスクリプトであるとは知らないか気にしません)は動作を停止しません。
キーストンプソン

5
それが動作しない場合/usr/bin/perl、私は非常に迅速に見つけます、そしてそれを最新に保つことはシステム所有者/管理者の責任です。自分のperlでスクリプトを実行する場合は、コピーを取得して変更するか、を介して呼び出してくださいperl foo。(。そして、あなたはこの答えをupvoted 55人がまた一つや二つ知っているそれは、あなたは正しいと、彼らはすべて間違っていることは確かに可能ですが、それは私が賭けたい方法ではない可能性を検討することがあります。)
キーストンプソン

101

/ usr / bin / envはを解釈できるため$PATH、スクリプトの移植性が高まります。

#!/usr/local/bin/python

Pythonが/ usr / local / binにインストールされている場合にのみスクリプトを実行します。

#!/usr/bin/env python

を解釈し$PATH、の任意のディレクトリでpythonを見つけます$PATH

だからあなたのスクリプトは、よりポータブルであり、パイソンは次のようにインストールされているシステム上で変更することなく動作します/usr/bin/python、または/usr/local/bin/python、または(に追加されたとしても、カスタムディレクトリ$PATHなど)/opt/local/bin/python

envハードコーディングされたパスよりも使用が推奨される唯一の理由は、移植性です。


19
python実行可能ファイルのカスタムディレクトリは、virtualenv使用率が増加するにつれて特に一般的です。
Xiong Chiamiov

1
では #! python、なぜ使用されないのですか?
クリスチャンプ

6
#! pythonベアワードpythonはファイルへの完全なパスとして解釈されるため、Pythonバイナリと同じディレクトリにいる必要があるため、使用されません。現在のディレクトリにpythonバイナリがない場合、次のようなエラーが表示されますbash: ./script.py: python: bad interpreter: No such file or directory。それはあなたが使用する場合と同じだ#! /not/a/real/path/python
ティム・ケネディ

47

絶対パスの指定は、特定のシステムでより正確です。欠点は、精度が高すぎることです。Perlのシステムインストールがスクリプトに対して古すぎることを認識しており、代わりに独自のものを使用する場合、スクリプトを編集してに変更#!/usr/bin/perlする必要があるとします#!/home/myname/bin/perl。あなたがPerlを持っている場合はさらに悪いことに、/usr/binいくつかのマシン上で、/usr/local/bin他の人に、そして/home/myname/bin/perlまだ他のマシン上で、あなたはスクリプトの3つの別々のコピーを維持し、各マシン上で適切なものを実行する必要があると思います。

#!/usr/bin/envPATH悪い場合は壊れますが、ほとんど何でも壊れます。悪意を持って操作しようとすることPATHは非常にまれであり、スクリプトが実行されているシステムについてほとんど知識がないため、絶対パスに頼ることはできません。

そこその場所あなたは、ほぼすべてのUNIXバリアント上に頼ることができる2つのプログラムは以下の通りです/bin/sh/usr/bin/env。いくつかのあいまいで、ほとんどが廃止されたU​​nixのバリアントには、が/bin/envなくても/usr/bin/envありましたが、それらに遭遇する可能性は低いでしょう。現代のシステムは、/usr/bin/envまさにシバンで広く使用されているためです。/usr/bin/env頼りになるものです。

を除いて/bin/sh、シバンで絶対パスを使用する必要があるのは、スクリプトが移植性を意図していない場合のみであるため、インタープリターの既知の場所に頼ることができます。たとえば、Linuxでのみ動作するbashスクリプトは、安全に使用できます#!/bin/bash。社内でのみ使用することを意図したスクリプトは、社内の通訳者の場所の規則に依存できます。

#!/usr/bin/env欠点があります。絶対パスを指定するよりも柔軟ですが、インタープリター名を知っている必要があります。場合によっては$PATH、スクリプトに関連する場所など、にないインタープリターを実行したい場合があります。このような場合、多くの場合、標準シェルと目的のインタープリターの両方で解釈できる多言語スクリプトを作成できます。たとえば、Python 2スクリプトpythonがPython 3でpython2Python 2であるシステムpythonとPython 2でpython2存在しないシステムの両方に移植可能にする場合:

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

22

特にperlの場合、使用すること#!/usr/bin/envは2つの理由で悪い考えです。

まず、ポータブルではありません。一部の不明瞭なプラットフォームでは、envは/ usr / binにありません。第二に、キース・トンプソンが指摘したように、シバン・ラインで引数を渡すことに問題を引き起こす可能性があります。最大限にポータブルなソリューションは次のとおりです。

#!/bin/sh
exec perl -x "$0" "$@"
#!perl

機能の詳細については、「perldoc perlrun」および-x引数についての説明を参照してください。


まさに私が探していたanser:perlに渡される追加の引数を許可するperlスクリプト用の、ポアブルな「シェバン」の書き方(この例の最後の行は追加の引数を受け入れます)。
-AmokHuginnsson

15

2つの間に区別がある理由は、スクリプトの実行方法のためです。

使用すると/usr/bin/env(他の回答で述べたように、中にされていない、/usr/binあなただけの後に実行可能ファイル名を置くことができないので、いくつかのOS上で)必要とされ#!、それは絶対パスでなければなりません- 。これは、#!メカニズムがシェルよりも低いレベルで機能するためです。これは、カーネルのバイナリローダーの一部です。これはテストできます。これをファイルに入れて実行可能にマークします。

#!bash

echo 'foo'

実行しようとすると、次のようなエラーが出力されます。

Failed to execute process './test.sh'. Reason:
The file './test.sh' does not exist or could not be executed.

ファイルが実行可能とマークされ、a #!で始まる場合、カーネル($PATHまたは現在のディレクトリを知らない:これらはユーザーランドの概念です)は絶対パスを使用してファイルを検索します。絶対パスの使用には問題があるため(他の回答で述べたように)、誰かがトリックを思いつきました:(/usr/bin/envほとんどの場合、その場所で)を実行して、を使用して何かを実行できます$PATH


11

使用にはさらに2つの問題があります #!/usr/bin/env

  1. インタプリタへのフルパスを指定する問題を解決するのではなく、単にに移動しenvます。

    envである/usr/bin/envことbashが保証されている/bin/bashか、Pythonであることが保証されています/usr/bin/python

  2. env ARGV [0]をインタープリターの名前(たとえば、bashまたはpython)で上書きします。

    これにより、スクリプトの名前がps出力などに表示されるのを防ぎ(または、表示方法や場所を変更する)、たとえば、ps -C scriptname.sh

[2016-06-04更新]

そして第三の問題:

  1. PATHの変更は、スクリプトの最初の行を編集するだけではなく、特にそのような編集のスクリプトを作成するのが簡単な場合は、より多くの作業が必要です。例えば:

    printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py

    ディレクトリを$ PATHに追加または事前に追加するのはかなり簡単です(ただし、ファイルを編集して永続的なものにする~/.profile必要があります-自分の名前を問わず)。 、最初の行ではありません)。

    PATHディレクトリの順序を変更することは非常に困難です#!

    そして、あなたはまだ使用#!/usr/bin/envがあなたに与える他のすべての問題を抱えています。

    @jlliagreは、#!/usr/bin/env複数のインタープリターバージョンでスクリプトをテストするのに役立つコメントで、「PATH / PATHの順序を変更するだけで」と提案しています

    それが必要な場合#!は、スクリプトの先頭に複数の行(最初の行以外の任意の場所にあるコメントのみ)を使用して、今使用したい行をカット/コピーアンドペーストする方がはるかに簡単です最初の行。

    ではvi、カーソルを目的の#!行に移動してから、入力するdd1GPか、Y1GP。のような些細なエディタでもnano、マウスを使用してコピーして貼り付けるのに数秒かかります。


全体として、使用することの利点は#!/usr/bin/envせいぜい最小限であり、確かに欠点を上回ることさえありません。「便利さ」の利点でさえ、ほとんど幻想的です。

IMOは、オペレーティングシステムは動作するものではないと考えている特定の種類のプログラマーによって推進された馬鹿げたアイデアです。 回避する必要のある(またはせいぜい無視される)問題です。

PS:これは、複数のファイルのインタープリターを一度に変更する簡単なスクリプトです。

change-shebang.sh

#!/bin/bash

interpreter="$1"
shift

if [ -z "$(type -P $interpreter)" ] ; then
  echo "Error: '$interpreter' is not executable." >&2
  exit 1
fi

if [ ! -d "$interpreter" ] && [ -x "$interpreter" ] ; then
  shebang='#!'"$(realpath -e $interpreter)" || exit 1
else
  shebang='#!'"$(type -P $interpreter)"
fi

for f in "$@" ; do
  printf "%s\n" 1 i "$shebang" . w | ed "$f"
done

たとえば、change-shebang.sh python2.7 *.pyまたはchange-shebang.sh $HOME/bin/my-experimental-ruby *.rb


4
ここで、ARGV [0]の問題について言及している人はいません。また、/ path / to / envの問題について、それを使用するための引数の1つに直接対処する形式で言及している人はいません(つまり、bashまたはperlが予期しない場所にある可能性があります)。
cas

2
インタープリターパスの問題は、シンボリックリンクを使用してsysadminを使用するか、ユーザーがスクリプトを編集することで簡単に修正できます。ここや他の質問で言及されているシバンの行でenvを使用することによって引き起こされる他のすべての問題に人々が耐えるのを促すほどの重大な問題ではありません。それは、cshスクリプティングの奨励が悪い解決策を促進するのと同じような方法で悪い解決策を促進することです。それは一種の働きをしますが、はるかに良い代替案があります。
cas

3
非システム管理者は、システム管理者に依頼することができます。または、単にスクリプトを編集して#!行を変更できます。
cas

2
それはまさにenvが回避するのに役立っているものです。sysadminやOSに依存する、pythonなどとは無関係の技術的知識に依存します。
jlliagre

1
私はこれを1000回賛成できたらと思っています。なぜなら、それは実際にはどちらかの方法で素晴らしい答えだからです-誰かが/usr/bin/envそれを使用していて、ローカルでそれを取り除く必要がある場合、または彼らがそれを使用しなかったので追加する必要がある場合。両方のケースが発生する可能性があるため、この回答で提供されるスクリプトは、ツールボックスに含めると便利なツールです。
-jstine

8

ここに別の例を追加します。

たとえばenv、複数のrvm環境間でスクリプトを共有する場合にも、使用は便利です。

これをcmd行で実行すると#!/usr/bin/env ruby、スクリプト内で使用されたときに使用されるrubyバージョンが示され ます。

env ruby --version

したがって、を使用するとenv、スクリプトを変更せずに、rvmを介して異なるrubyバージョンを使用できます。


1
//素晴らしいアイデア。複数のインタープリターの全体的なポイントは、コードを壊すことではなく、コード 特定の インタープリターに依存することではありません。
ネイサンバサネーゼ

4

自分自身または仕事のためだけに書いており、呼び出しているインタープリターの場所が常に同じ場所にある場合は、必ず直接パスを使用してください。それ以外の場合はすべてを使用します#!/usr/bin/env

その理由は次のとおりです。あなたの状況では、python使用した構文に関係なくインタープリターは同じ場所にありましたが、多くの人にとっては別の場所にインストールすることもできました。ほとんどの主要なプログラミングインタプリタは/usr/bin/多くの新しいソフトウェアにありますが、デフォルトではデフォルトになってい/usr/local/bin/ます。

#!/usr/bin/env同じインタープリターの複数のバージョンがインストールされていて、シェルのデフォルトがわからない場合は、おそらくそれを修正する必要があるため、常に使用することを主張します。


5
「他のすべてのケースでは#!/ usr / bin / envを使用」は強すぎます。// @KeithThompsonなどが指摘しているように、#!/ usr / bin / envは、誰/どのように実行するかによってスクリプトの動作が異なることを意味します。この異なる動作がセキュリティバグになる場合があります。//たとえば、setuidまたはsetgidスクリプトに「#!/ usr / bin / env python」を使用しないでください。これは、スクリプトを呼び出すユーザーがPATHの「python」というファイルにマルウェアを配置できるためです。setuid / gidスクリプトが良いアイデアであるかどうかは別の問題ですが、確かに、setuid / gid実行可能ファイルはユーザーが提供する環境を信頼するべきではありません。
クレイジーグリュー

@ KrazyGlew、Linuxでスクリプトをsetuidすることはできません。実行可能ファイルを介して実行します。また、この実行可能ファイルを作成するときは、環境変数をクリアすることをお勧めします。一部の環境変数も意図的に無視されます。
MayeulC

3

移植性と互換性の理由から、使用することをお勧めします

#!/usr/bin/env bash

の代わりに

#!/usr/bin/bash

Linux / Unixシステム上にバイナリが配置される可能性のある複数の可能性があります。ファイルシステム階層の詳細な説明については、hier(7)マンページを確認してください。

たとえば、FreeBSDは、ベースシステムの一部ではないすべてのソフトウェアを/ usr / local /にインストールします。bashは基本システムの一部ではないため、bashバイナリは/ usr / local / bin / bashにインストールされます。

ほとんどのLinuxディストリビューションとFreeBSDで実行される移植可能なbash / csh / perl / whateverスクリプトが必要な場合は、#!/ usr / bin / envを使用する必要があります。

また、ほとんどのLinuxインストールでは、envバイナリが/ bin / envに(ハード)リンクされているか、/ usr / binが/ binにソフトリンクされているため、シバンでは使用しないでください。したがって、#!/ bin / envを使用しないでください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.