「#!」の「-」/ bin / sh-” shebang?


27
#!/ bin / sh-

によって解釈されるスクリプトを使用することが推奨されるシバンです(または少なくともそうでした)/bin/sh

なぜだけでは#! /bin/shないの#!/bin/shですか?

それは何の-ためですか?

回答:


37

それはあなたが書く必要がある理由と同じ理由です:

rm -- *.txt

ではない

rm *.txt

.txt現在のディレクトリ内のファイルの名前がで始まることを保証できない場合を除きます-

に:

rm <引数>

<arg>で始まる場合はオプションと見なされ、-そうでない場合は削除するファイルと見なされます。に

rm- <引数>

argで始まるかどうかに関係なく、常に削除するファイルと見なされます-

それはについても同じですsh

で始まるスクリプトを実行するとき

#! /bin/sh

通常:

execve("path/to/the-script", ["the-script", "arg"], [environ])

システムはそれを次のように変換します。

execve("/bin/sh", ["/bin/sh", "path/to/the-script", "arg"], [environ])

path/to/the-script通常、スクリプトの作者の制御下にあるものではありません。作成者は、スクリプトのコピーが保存される場所や名前を予測できません。特に、path/to/the-script呼び出し元がで始まらないことを保証することはできません-(または、+これも問題ですsh)。私たちが理由です必要-なオプションの終わりをマークするためにここに。

たとえば、私のシステムではzcat(実際に他のほとんどのスクリプトと同様に)、その推奨事項に従わなかったスクリプトの一例です。

$ head -n1 /bin/zcat
#!/bin/sh
$ mkdir +
$ ln -s /bin/zcat +/
$ +/zcat
/bin/sh: +/: invalid option
[...]

今、あなたはなぜそう#! /bin/sh -ではないの#! /bin/sh --かと尋ねるかもしれません。

一方で#! /bin/sh --POSIXシェルで機能するであろう、#! /bin/sh -よりポータブルです。特に古代バージョンのsh。オプションの終了sh前日-として処理し、長い間オプションの終了をマークするgetopt()ための一般的な使用--。Bourneシェル(70年代後半以降)が引数を解析する方法で、最初の引数のみがで始まる場合にオプションとして考慮されました-。以降のすべての文字-はオプション名として扱われます。の後に文字がない場合-、オプションはありません。それは止まり、後のすべてのBourne風のシェル-はオプションの終わりをマークする方法として認識します。

Bourneシェル(ただし、現代のBourneのようなシェルではない)では、#! /bin/sh -eufオプションの最初の引数のみが考慮されたため、問題を回避できます。

ここで、私たちはここで行していると言うことができます。 上記の斜体で必要性あります:

  1. 彼らの正しい心の誰もが何かで始まるスクリプトを呼び出すことはありません -か、+または名前がで始まるディレクトリに入れ-たり+
  2. たとえ彼らがそうだとしても、最初は自分自身を責めることしかできないと主張しますが、スクリプトを呼び出すときは、多くの場合、それはシェルまたはexecvp()/ execlp()型関数からです。そして、その場合には、一般的に、あなたは、のいずれかとして、それらを呼び出すthe-scriptことがでルックアップさせるため$PATHにケース内パス引数execve()のシステムコール、一般的に開始します/(ない-+)またはとして./the-scriptあなたがしたい場合はthe-script(カレントディレクトリで実行すると、その場合、パスはで始まらず、どちらでもあり./ませ-+)。

理論的な正確性の問題に加えて、グッドプラクティス#! /bin/sh -として推奨されるようになったもう1つの理由があります。そして、それはいくつかのシステムがまだsetuidスクリプトをサポートしていた時代にさかのぼります。

次を含むスクリプトがある場合:

#! /bin/sh
/bin/echo "I'm running as root"

そして、そのスクリプトは-r-sr-xr-x root bin、通常のユーザーによって実行された場合、それらのシステム上でsetuid root(パーミッション付きなど)でした。

execve("/bin/sh", ["/bin/sh", "path/to/the-script"], [environ])

として行われrootます!

ユーザーがシンボリックリンク/tmp/-i -> path/to/the-scriptを作成してそれを実行した場合-i、対話型シェル(/bin/sh -i)を起動しrootます。

これ-は回避できます(競合状態の問題や、一部のsh実装のような一部の実装ksh88がスクリプト引数を参照せず/に検索するという事実は回避できません$PATH)。

今日では、ほとんどのシステムサポートのsetuidスクリプトもはや、および(ないデフォルトでは、通常は)まだやるもののいくつかは、やって終わるexecve("/bin/sh", ["/bin/sh", "/dev/fd/<n>", arg])(ここで、<n>周りにこの問題との両方で動作しますスクリプトに読み取るためのオープンファイル記述子です)競合状態。

Bourneのようなシェルだけでなく、ほとんどのインタープリターでも同様の問題が発生することに注意してください。非Bourne風のシェルは、一般に-オプション終了マーカーとしてサポートしていませんが、--代わりにサポートしています(少なくとも最新バージョンでは)。

また

#! /usr/bin/awk -f
#! /usr/bin/sed -f

次の引数は、引数として考えられているような問題をお持ちでない-fいずれの場合オプションが、あれば、それはまだ仕事をしないpath/to/script-と、その場合には(ほとんどのsed/ awk実装、sed/awkからのコードを読んでいません-ファイル、しかし代わりに標準入力から)。

また、ほとんどのシステムでは以下を使用できないことに注意してください。

#! /usr/bin/env sh -
#! /usr/bin/perl -w --

ほとんどのシステムと同様に、シバンメカニズムでは、インタープリターパスの後に引数を1つだけ許可します。

#! /bin/sh -vs を使用するかどうかは#!/bin/sh -、単に好みの問題です。前者のほうが、インタープリターパスがより見やすくなり、マウスの選択が容易になるためです。一部の古代のUnixバージョンではスペースが必要だっという伝説がありますが、これは確認されていないためです。

Unixシバングに関する非常に良いリファレンスは、https: //www.in-ulm.de/~mascheck/various/shebangにあります。


1
これは#!/ bin / bashと関連がありますか?
ジョー

1
もちろん、@ Joeはbash他のすべてのシェルと同様にオプションを受け入れます。BourneのようなPOSIXシェルであるため、とのbash両方#! /bin/bash -を受け入れます#! /bin/bash --
ステファンシャゼラス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.