他の人から入手したスクリプトの中には、シバン#!/path/to/NAME
他の人(同じツールNAMEを使用)がシバンを持っている#!/usr/bin/env NAME
。
どちらも正常に動作しているようです。チュートリアル(Pythonなど)では、後者のシバンの方が優れているという提案があるようです。しかし、なぜそうなのかよくわかりません。
最初のシバンにはこの制限がないのに対し、後者のシバンを使用するには、NAMEがパスに含まれている必要があることを理解しています。
また、NAMEの場所を正確に指定しているため、(私には)最初の方がシバンの方が良いようです。したがって、この場合、NAMEのバージョンが複数ある場合(たとえば、/ usr/bin/NAME、/ usr/local/bin/NAME)、最初のケースでどちらを使用するかを指定します。
私の質問は、なぜ最初のシバンが2番目のものより好ましいのですか?
absoluteまたはlogical(_/usr/bin/env
_)シバのインタープリターへのパス、(2)の重要な考慮事項があります:
a)インタプリタはターゲットシステムで見つけることができます
b)正しいバージョンインタプリタターゲットシステムで見つけることができます
もし私たちが[〜#〜]同意する[〜#〜]その "b) "が望ましい、私たちも同意する:
c)不正なインタープリターバージョンを使用して実行し、一貫性のない結果を得る可能性があるのではなく、スクリプトfailが望ましい。
我々が同意しないその「b)」が重要である場合、見つかった通訳で十分です。
logicalpath- _/usr/bin/env
_を使用しているため、シバンのインタプリタは同じスクリプトが同じインタープリターへの異なるパスを持つターゲットホストで正常に実行できるようにする最も拡張性の高いソリューションです。Pythonを使用しているので、それを使用してテストします。私たちの基準。
_/usr/bin/env
_は[〜#〜] popular [〜#〜]の予測可能な一貫した場所に存在しますか( "every ")オペレーティングシステム? はい:
以下Pythonスクリプトは仮想エンベロープの内側と外側の両方で実行されます(Pipenv使用済み)テスト中:
#!/usr/bin/env pythonX.x import sys print(sys.version) print('Hello, world!')
スクリプトのシバンはPython必要なバージョン番号(すべて同じホストにインストールされています)によって切り替えられました):
期待される結果:そのprint(sys.version)
= _env pythonX.x
_。インストールされている別のPython=バージョンを使用して_./test1.py
_が実行されるたびに、she-bangで指定された正しいバージョンが出力されました。
テストノート:
/usr/bin
_に存在します_#!/usr/bin/env python
_がユーザーのパスにあるPythonの最初のバージョンを使用することはTRUEですが、_#!/usr/bin/env pythonX.x
_などのバージョン番号を指定することでこの動作を緩和できます。実際、開発者は「first」がどのインタープリターであるかを気にしません。開発者が気にするのは、コードと互換性があることがわかっている特定のインタープリターを使用してコードが実行されることです。ファイルシステムに存在する可能性があるすべての場所で一貫した結果を保証します......
移植性/柔軟性の観点から、logical-_/usr/bin/env
_-absoluteパスは要件のa)、b)およびc)を満たしているだけでなく、 Pythonですが、異なるオペレーティングシステムの異なるパスにある場合でも、同じバージョンのインタープリターをファジーロジックで検索できるという利点もあります。 そして[〜#〜] most [〜#〜]ディストリビューションはFHSを尊重しますが、すべてがそうではありません 。
したがって、バイナリが別の場所に存在する場合、スクリプトは[〜#〜]失敗[〜#〜]しますabsolute次に、Shebangで指定されたパス、logicalパス[〜#〜] [〜#〜]は、それが見つかるまで続行するため、一致するため、プラットフォーム全体で信頼性と拡張性が向上します。
必ずしも良いとは限りません。
#!/usr/bin/env python
の利点は、ユーザーの$PATH
に最初に現れるpython
実行可能ファイルを使用することです。
#!/usr/bin/env python
の-欠点は、ユーザーの$PATH
に最初に現れるpython
実行可能ファイルを使用するということです。
つまり、スクリプトの実行者によってスクリプトの動作が異なる可能性があります。 1人のユーザーの場合、OSと共にインストールされた/usr/bin/python
を使用する可能性があります。もう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を含む)には、/usr/bin
にenv
コマンドがありませんでした。これらのどちらも重要な問題になることはありません。 env
はこのように機能します。多くのスクリプトは#!/usr/bin/env
トリックを使用しており、OSプロバイダーはそれを壊すために何もしそうにありません。 可能性スクリプトを本当に古いシステムで実行したい場合は問題になりますが、とにかくそれを変更する必要があるでしょう。
もう1つの考えられる問題(コメントで指摘してくれたSopalajo de Arrierezに感謝)は、cronジョブが制限された環境で実行されることです。特に、$PATH
は通常、/usr/bin:/bin
のようなものです。したがって、インタープリタを含むディレクトリがユーザーのシェルのデフォルトの$PATH
にある場合でも、これらのディレクトリのいずれにもない場合、/usr/bin/env
トリックは機能しません。正確なパスを指定するか、crontabに行を追加して$PATH
(詳細はman 5 crontab
)を設定できます。
/ usr/bin/envは_$PATH
_を解釈できるため、スクリプトの移植性が向上します。
_#!/usr/local/bin/python
_
pythonが/ usr/local/binにインストールされている場合のみ、スクリプトを実行します。
_#!/usr/bin/env python
_
_$PATH
_を解釈し、_$PATH
_の任意のディレクトリでpythonを見つけます。
したがって、スクリプトはより移植性が高く、pythonが_/usr/bin/python
_、または_/usr/local/bin/python
_、または追加されたカスタムディレクトリ)としてインストールされているシステムで変更なしで動作します_$PATH
_のように_/opt/local/bin/python
_)に変換します。
ハードコーディングされたパスよりもenv
を使用するほうが好ましい唯一の理由は、移植性です。
絶対パスの指定は、特定のシステムでより正確です。欠点は、精度が高すぎることです。 Perlのシステムインストールがスクリプトに対して古すぎるため、代わりに独自のものを使用したい場合は、スクリプトを編集して#!/usr/bin/Perl
を#!/home/myname/bin/Perl
に変更する必要があります。さらに悪いことに、一部のマシンでは/usr/bin
、他のマシンでは/usr/local/bin
、さらに他のマシンでは/home/myname/bin/Perl
でPerlを使用している場合、スクリプトの3つの別々のコピーを保持して実行する必要があります。各マシンで適切なもの。
#!/usr/bin/env
は、PATH
が悪いと壊れますが、ほとんど何でもします。不正なPATH
を使用して操作しようとすることはめったに役立ちませんが、スクリプトが実行されているシステムについてほとんどわかっていないため、絶対パスに頼ることはできません。
ほぼすべてのUNIXバリアントに依存できる場所として、/bin/sh
と/usr/bin/env
の2つのプログラムがあります。一部のあいまいでほとんど廃止されたUnixバリアントには、/bin/env
がなくても/usr/bin/env
がありましたが、それらに遭遇することはほとんどありません。現代のシステムは、シバンで広く使用されているため、/usr/bin/env
を持っています。 /usr/bin/env
は信頼できるものです。
/bin/sh
を除いて、Shebangで絶対パスを使用する必要があるのは、スクリプトが移植可能ではない場合だけなので、インタープリターの既知の場所を頼りにすることができます。たとえば、Linuxでのみ機能するbashスクリプトは#!/bin/bash
を安全に使用できます。社内でのみ使用することを意図したスクリプトは、社内通訳者の場所の規則に依存できます。
#!/usr/bin/env
には欠点もあります。絶対パスを指定するよりも柔軟性がありますが、インタプリタ名を知っている必要があります。場合によっては、$PATH
にないインタプリタを実行する必要がある場合があります。たとえば、スクリプトからの相対位置などです。このような場合、多くの場合、標準シェルと目的のインタープリターの両方で解釈できる polyglot script を作成できます。たとえば、Python 2スクリプトを、python
がPython 3であり、python2
がPython 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 …
特に、Perlの場合、#!/usr/bin/env
は2つの理由で悪い考えです。
まず、それは移植可能ではありません。一部のあいまいなプラットフォームでは、envは/ usr/binにありません。次に、 Keith Thompson が指摘したように、Shebang行で引数を渡すときに問題が発生する可能性があります。最大限にポータブルなソリューションはこれです:
#!/bin/sh
exec Perl -x "$0" "$@"
#!Perl
動作の詳細については、「perldoc perlrun」および-x引数についての説明を参照してください。
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.
_
ファイルが実行可能としてマークされ、_#!
_で始まる場合、カーネル(_$PATH
_または現在のディレクトリを知らない:これらはユーザーランドの概念です)は、絶対パスを使用してファイルを探します。絶対パスの使用は問題があるため(他の回答で述べたように)、誰かがトリックを思い付きました:_/usr/bin/env
_(ほとんどの場合、その場所にあります)を実行して、_$PATH
_を使用して何かを実行できます。
#!/usr/bin/env
の使用には、さらに2つの問題があります。
それはインタプリタへのフルパスを指定する問題を解決しません、それを単にenv
に移動します。
env
が/usr/bin/env
にあることが保証されているのは、bash
が/bin/bash
またはpython in /usr/bin/python
にあることが保証されています。
env
は、ARGV [0]をインタープリターの名前(bashやpythonなど)で上書きします。
これにより、スクリプトの名前がps
などの出力に表示されなくなり(または、表示方法や表示場所が変更され)、たとえばps -C scriptname.sh
でそれを見つけることができなくなります。
[2016年6月4日更新]
そして3番目の問題:
PATHを変更することは、スクリプトの最初の行を編集するだけではなく、もっと作業が多く、特にそのような編集のスクリプトを作成するのは簡単です。例えば:
printf "%s\n" 1 i '#!'$(type -P python2) . w | ed foo.py
$ PATHにディレクトリを追加またはプリペンドするのはかなり簡単です(ただし、ファイルを編集して永続化するには、~/.profile
などを使用する必要があります。また、PATHはどこにでも設定できるため、編集するスクリプトを作成するのは簡単ではありません。 1行目ではありません)。
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
として実行します。
ここに別の例を追加します。
env
の使用は、たとえば、複数のrvm
環境間でスクリプトを共有する場合にも役立ちます。
これをcmd行で実行すると、#!/usr/bin/env Ruby
がスクリプト内で使用されている場合に、どのRubyバージョンが使用されるかがわかります。
env Ruby --version
したがって、env
を使用すると、スクリプトを変更せずに、rvmを介して異なるRubyバージョンを使用できます。
自分または仕事のためだけに書いていて、呼び出すインタプリタの場所が常に同じ場所にある場合は、必ずダイレクトパスを使用してください。その他の場合はすべて#!/usr/bin/env
を使用します。
理由は次のとおりです。あなたの状況では、python
インタープリターは、使用した構文に関係なく同じ場所にありましたが、多くの人にとっては、別の場所にインストールされている可能性があります。ほとんどの主要なプログラミングインタープリターは/usr/bin/
にありますが、新しいソフトウェアの多くはデフォルトで/usr/local/bin/
になっています。
同じインタープリターの複数のバージョンがインストールされていて、シェルのデフォルトの設定がわからない場合は、おそらく#!/usr/bin/env
を使用するようにさえ主張しますが、おそらくそれを修正する必要があります。
移植性と互換性の理由から、使用することをお勧めします
#!/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にソフトリンクされているため、notを使用する必要があります。シバンで。 #!/ bin/envは使用しないでください。