Bourneシェルがディストリビューションで利用できない場合、hashbangで/ bin / shを使用するのは正しいですか?


15

一般に、シェルスクリプトには、スクリプトファイルの最初の行に次のコメントが含まれています#!/bin/sh。私が行った研究によれば、これは「ハッシュバン」と呼ばれ、従来のコメントです。このコメントは、このファイルがディレクトリ下のBourne Shellによって実行されることをUnixに通知します/bin

私の質問はその点から始まります。今までこのようなコメントを見たことはありません#!/bin/bash。いつも#!/bin/shです。ただし、UbuntuディストリビューションにはBourne Shellプログラムがありません。Bourne Again Shell(bash)があります。

その点で、#!/bin/shUbuntuディストリビューションで書かれたシェルスクリプトにコメントを入れるのは正しいですか?


4
aka

1
このserverfaultの質問への私の答えを参照してください:#!/ bin / sh vs#!/ bin / bash最大限の移植性
ゴードンデイヴィ

より一般的に呼ばれるshebang
fpmurphy

回答:


16

#!/bin/shすべてのUnixおよびUnixライクなディストリビューションで動作するはずです。一般に、スクリプトがPOSIXに準拠している限り、最も移植性の高いハッシュバンと考えられています。/bin/shシェルは関係なく、実際の殻のようにその仮面舞踏会であるものの、道具POSIXシェル標準シェルそのことになっている/bin/shシェル。


#!/bin/shBourneシェルが維持されなくなったため、通常は単なるリンクになりました。多くのUnixシステム/bin/shへのリンクになります/bin/ksh/bin/ash、それはへのリンクになります多くのRHELベースのLinuxシステムでは/bin/bash、しかし、Ubuntuと多くのDebianベースのシステムで、それはへのリンクです/bin/dash。すべてのシェル、として起動された場合sh、POSIX互換モードに入ります。

ただし、スクリプトが厳密にPOSIX準拠である限り(重要性を強調するために繰り返される)、ハッシュバンは他のメソッドよりもはるかに高い移植性を可能にするため、重要なプレースホルダーです。

注:bashPOSIXモードで呼び出された場合、、[[配列など、POSIX以外のものも引き続き許可されます。これらはbashシステム以外では失敗する可能性があります。


3
「Ubuntuでは/ bin / dashへのリンク」であり、一般的には他のDebianベースのシステムです。FreeBSD / GhostBSD上のIIRC /bin/ashも同様にシンボリックリンクです。
セルギーKolodyazhnyy

完全に移植可能にするには、シバンと(絶対)インタープリターパスの間にスペースを入れます。一部のシステム#! /は、単にの代わりに検索し#!ます。
サイモン・リヒター

5
@SimonRichterこれについてのリファレンスは、見た方がいいでしょう。
クサラナナンダ

@SergiyKolodyazhnyy FreeBSDにインストールされたshのバージョンは灰であり、シンボリックリンクではありません。
ロブ

2
@Kusalananda、うーん、このページはこれは神話だと言っているので、おそらく無視できるでしょう。
サイモンリヒター

12

あなたはそれを後方に持っています。/bin/sh最近ではほとんどボーンシェルではありません。シバンを使用するときに問題が発生するのは¹です#! /bin/sh

Bourneシェルは70年代後半に書かれたシェルで、以前のThompsonシェル(別名: sh)。80年代前半、David KornはBourneシェルのいくつかの拡張機能を作成し、いくつかのバグと設計の不便さを修正し(さらにいくつかを導入し)、Kornシェルと呼びました。

90年代前半、POSIXは言語を指定しましたsh はKornシェルのサブセットに基づいをほとんどのシステムが/bin/shKornシェルまたはその仕様に準拠したシェルに変更されました。BSDの場合/bin/sh、最初に(ライセンス上の理由でBourneシェルを使用できなくなった後)、Almquistシェル(ksh拡張機能を備えたBourneシェルのクローン)を徐々に変更し、POSIX準拠になりました。

現在、すべてのPOSIXシステムには、shほとんどの場合POSIXに準拠しているシェル(ほとんどの場合、必ずしもそうで/binはありませんが、POSIXは指定したユーティリティのパスを指定しません)があります。通常、ksh88、ksh93、pdksh、bash、ash、zsh²のいずれかに基づいていますが、BourneシェルはPOSIX準拠ではないため、Bourneシェルには基づいていません³。これらのシェル(の数bashzshyashおよびいくつかのpdksh誘導体は、として呼び出されたときにPOSIX準拠モードを有効にshしています以下別段準拠)。

bash(Kornシェルに対するGNUの回答)は実際には唯一のオープンソースシェルです(他のシェルは一般的に90年代以降新しい機能を取得していないksh88に基づいているため、現在維持されていると言えます)。 POSIX準拠sh(macOS認定の一部として)。

#! /bin/sh -she-bangを使用してスクリプトを作成するときは、標準sh構文を使用する必要があります(また、移植性が必要な場合は、そのスクリプトで使用されるユーティリティの標準構文も使用する必要があります。シェルの解釈に関与するのはシェルだけではありません)スクリプト)、その後、その標準sh構文インタープリターの実装が使用されているかどうかは関係ありません(kshbash...)。

それらを使用しない限り、それらのシェルが標準を超える拡張機能を持っていることは問題ではありません。標準のCコードを記述し、一方のコンパイラ(などgcc)の拡張機能を使用しない限り、Cコードを記述するようなものです。コンパイラが準拠していれば、コンパイラの実装に関係なくコードをコンパイルできます。

ここで#! /bin/sh、あなたの主な問題/bin/shは、Bourneシェルが存在するシステムです。たとえば$((1+1))$(cmd)などの標準機能をサポートしていない${var#pattern}場合、次のような回避策が必要になる場合があります。

#! /bin/sh -
if false ^ true; then
  # in the Bourne shell, ^ is an alias for |, so false ^ true returns
  # true in the Bourne shell and the Bourne shell only.
  # Assume the system is Solaris 10 (older versions are no longer maintained)
  # You would need to adapt if you need to support some other system
  # where /bin/sh is the Bourne shell.
  # We also need to fix $PATH as the other utilities in /bin are
  # also ancient and not POSIX compatible.
  PATH=`/usr/xpg6/bin/getconf PATH`${PATH:+:}$PATH || exit
  export PATH
  exec /usr/xpg4/bin/sh "$0" "$@"
  # that should give us an environment that is mostly  compliant
  # to the Single UNIX Specification version 3 (POSIX.2004), the
  # latest supported by Solaris 10.
fi
# rest of the script

ところで、Ubuntu /bin/shbashデフォルトではありません。それはだdash、これらの日、NetBSDのに基づいており、シェルsh、主にそれがマルチバイト文字をサポートしていないことを除いて、POSIX準拠のAlmquistシェルに基づいて、自分自身。Ubuntuと他のDebianベースのシステムでは、から選択することができますbashし、dashために/bin/shdpkg-reconfigure dash4shDebianに付属のスクリプトは、Debianポリシー標準(POSIX標準のスーパーセット)に記述されているため、両方のシェルで同じように動作するはずです。おそらく、それらはzshshエミュレーションでも正常に動作しますbosh(おそらくそうではなく、組み込みksh93yashありませんlocal(Debianポリシーで必要ですがPOSIXでは必要ありません))。

unix.stackexchange.comのスコープ内のすべてのシステムには、POSIXがsh どこかにあります。それらのほとんどは/bin/sh/binディレクトリを持たない非常にまれなものを見つけるかもしれませんが、おそらくそれを気にしないでしょう)、そしてそれは一般にPOSIXですshインタープリターです(そしてまれに(代わりに(非標準)Bourneシェルです) )。

しかしsh、システム上で確実に見つけることができる唯一のシェルインタープリタ実行可能ファイルです。他のシェルについては、macOS、Cygwin、およびほとんどのGNU / Linuxディストリビューションにが確実に含まれますbash。SYSVから派生したOS(Solaris、AIX ...)には通常、ksh88、場合によってはksh93が含まれます。OpenBSD、MirOSにはpdksh派生物があります。macOSにはがありzshます。しかし、それ以外では、保証はありません。bashこれらの他のシェルがインストールされるかどうかの保証はありません/bin(通常は/usr/local/binたとえば、インストール時にBSDでられます)。そしてもちろん、インストールされるシェルのバージョンの保証はありません。

#! /path/to/executableこれは慣習ではなく、すべてのUnixライクカーネルの機能であることに注意してください(80年代初期にデニスリッチーによって導入されました)の機能であり、で始まる最初の行でインタープリターのパスを指定することで任意のファイルを実行できることに#!。任意の実行可能ファイルにすることができます。

最初の行がで始まるファイルを#! /some/file some-optional-arg実行する/some/filesome-optional-arg、カーネルはスクリプトのパスと元の引数を引数として実行されます。その最初の行#! /bin/echo testを作成して、何が起こっているのかを確認できます。

$ ./myscript foo
test ./myscript foo

あなたが使用する場合/bin/sh -の代わりに/bin/echo test、カーネルの実行は/bin/sh - ./myscript fooshコンテンツに格納されたコードを解釈myscriptし、無視し、それはコメント(開始とだとして、最初の行のことを#)。


¹おそらく、/bin/shBourneシェルをベースにした私たちが遭遇する唯一のシステムは、Solaris 10です。Solarisは、後方互換性のためにBourneシェルを保持することを決めた数少ないUnixの1つです(POSIX sh言語は完全ではありませんBourneシェルとの下位互換性)および(少なくともデスクトップおよびサーバー全体の展開の場合)POSIXはsh他の場所(/usr/xpg4/bin/shksh88に基づいて)にありますが、Solaris 11では/bin/shksh93に変更されました。他のものはほとんど機能していません。

² にするために使用のMacOS / Xのが、後に変更します。POSIX 実装として使用されるのは、主な焦点ではありません。そのモードは、主にPOSIX コードを埋め込みまたは呼び出すことができるようにすることです/bin/shzshbashzshshshsourceshzshスクリプト

³最近、@ schilyはOpenSolarisシェル(BourneシェルをベースにしたSVR4シェルのベース)をPOSIX準拠にまで拡張し、呼び出されましたboshが、まだどのシステムでも使用されていることを知りません。それに加えksh88て、Bourneシェルのコードに基づく2番目のPOSIX準拠のシェルになります

4古いバージョンでは、mkshPOSIXのより多くのlkshインカネーションを使用することもできます。それは、Fordkthシェル(Bourneシェルの別の再実装)に基づくpdkshに基づくMirOS(以前のMirBSD)シェルです)


些細なこと(答えの途中のシェルコードについて):exec sh "$0" "$@"を設定した直後に使用すべきではありませんPATHか?それはsh正しい場所から拾い上げるべきだと思いました。
クサラナナンダ

1
@Kusalananda、それは動作するはずですが、何かが間違っていると無限ループに陥る可能性があるため、よりリスクが高くなります(権限が間違っているsh、getconfが変更されているなど)。とにかく、残りはSolaris 10に固有であるため、のコンテンツを含むすべてをハードコーディングすることもでき$PATHます。
ステファンシャゼル

POSIXシェルとしてkshを使用するOSXの引用。デフォルトのシェルはtcshのを可能に使用されるが、私はOSXが出てきた後までKornシェルは、オープンソースではなかったとして、特に使用されていないbashの覚えていない
user151019

1
@ Mark、OSXがkshを使用したことはなかった。以前はzshでしたが、bash(bashの特別なビルド)に切り替えました。
ステファンシャゼラス

1
@fpmurphy、初期バージョンを見ると、kshがBourneシェルと互換性がない場合は、bashはすでにkshに隠れていました。bash2までksh88と同じ機能レベルに到達するまで待たなければなりませんでしたが、bashには当初、、$(...)emacs / viモード、チルダ/ブレース拡張(最初はcshからの拡張)、fc、typeset、alias などのkshの拡張機能がいくつかありました、kshの形式の関数の構文...
ステファンChazelas

8

はい#!/bin/sh、スクリプトで使用できます。/bin/shそのようなシステムでは、通常はbash(多かれ少なかれ)振る舞うようなリンクを介して提供されるためshです。たとえば、リンクするCentos7システムを次に示します。shbash

-bash-4.2$ ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Dec  4 16:48 /bin/sh -> bash
-bash-4.2$ 

また、そのシステム専用#!/bin/bashbashスクリプトを作成していて、bash機能を使用する場合にも使用できます。しかし、そのようなスクリプトは、OpenBSD上、たとえば、移植性の問題に悩まされますbash管理者は、それをインストールするには手間がかかる(私はしません)、その後、それがインストールされている場合にのみインストールされている/usr/local/bin/bash、ではありません/bin/bash。厳密にPOSIX #!/bin/shスクリプトを移植する必要があります。


これに注意してください:「として起動されるとsh、Bashはスタートアップファイルを読み込んだ後にPOSIXモードに入ります。」- gnu.org/software/bash/manual/bashref.html#Bash-POSIX-Mode
グレンはジャックマン

2
仕事でRHELサーバー用のスクリプトを書くときは、#!/bin/bashPOSIX以外の機能を活用できるように正確に使用します。
モンティハーダー

RHELの@MontyHarder IIRC /bin/shはシンボリックリンクですがbash、それは正しいですか?ただし、CentOSでは確かにそうです。
セルギーKolodyazhnyy

1
@SergiyKolodyazhnyyはい、チェックしたばかりのRHELサーバーでは、へのシンボリックリンクbashです。バイナリは、それを呼び出すために使用された名前を知っており、呼び出されるshと、昔ながらのBourneのように動作しshます。
モンティハーダー

6

あなたが尋ねた

コメント#!/ bin / shをubuntuディストリビューションで記述されたシェルスクリプトに配置するのは正しいですか?

答えは、シェルスクリプトに記述した内容によって異なります。

  • 移植可能なPOSIX準拠のスクリプトを厳密に使用し、bash固有のコマンドを使用しない場合は、次使用できます。/bin/sh

  • bashを使用するマシンでのみスクリプトを使用することがわかっている場合、bash固有の構文使用する必要がある場合は、/bin/bash

  • スクリプトがさまざまなUNIXマシンで動作することを確認したい場合は、POSIX準拠の構文のみ使用する必要があります。/bin/sh

  • あなたが定期的に使用している場合は、別のシェルを(例えばkshのzshのtcshの)、および、スクリプトでその構文を使用したい場合、あなたがすべき適切なインタプリタを使う(のような/bin/ksh93/bin/zshまたは/bin/tcsh


3

「#!」コメントは常に/bin/bashまたはを使用しません/bin/sh。シェルスクリプトだけでなく、インタプリタがどうあるべきかをリストします。たとえば、私のpythonスクリプトは通常、#!/usr/bin/env pythonます。

との違いは#!/bin/sh、に対するシンボリックリンク#!/bin/bash/bin/shはないこと/bin/bashです。多くの場合、常にではありません。ここではUbuntuは注目に値する例外です。私は、CentOSでスクリプトが正常に動作するのを見てきましたが、Ubuntuでは、作成者がでbash固有の構文を使用したために失敗しました#!/bin/sh


1

それ/bin/shすべてに存在するというすべての偉大な答えに冷たい水を注いで申し訳ありません Unixシステムに。

Androidのシェルは/system/bin/shにあり/bin/sh、ルート化されたシステム上であっても、リンクを作成する方法は通常ありません(システムがselinuxおよびcapabilities(7)バウンディングセットを使用してロックダウンされる方法のため)。

AndroidはPOSIXに準拠していないと言う人のために:ほとんどのLinuxおよびBSDディストリビューションもそうではありません。そして、/bin/shこのパスの存在は標準で義務付けられていません

アプリケーションは標準ことに注意すべきであるPATHシェルのいずれかであると仮定することができない/bin/sh、または/usr/bin/sh、との質問によって決定されるべきPATHによって返さgetconf PATH返されるパス名は、絶対パスとしない内蔵シェルであることを保証します。


POSIX準拠にしようとするすべてのシステムには多数の適合バグ(認定済みのものを含む)がありますが、Android(およびルーターや電球OSのような他のほとんどの組み込みシステム)はPOSIXに準拠するつもりはありません。ここでは、AndroidはPOSIXインターフェースに依存する場合を除き、トピック外です。
ステファンシャゼラス

@Christopher、どれgetconf?たとえば、Solaris 11.4では、それは1つ/usr/binですか?1つ/usr/xpg4/bin(SUSv2準拠)、1つ/usr/xpg6/bin(SUSv3準拠)?/usr/xpg7/bin(SUSv4用)?
ステファンシャゼル

@StéphaneChazelas私たちが意図を判断するなら、私は簡単にいくつかのLinus TorvaldsやTheo de Raadtの引用を掘り下げることができると思います。 + busybox。通常のUnixライクなシステムほどPOSIX準拠ではありません。そして、a)アンドロイドは実際には「組み込み」システムではありません。b)アンドロイドシステムはシェルを持たずに POSIX準拠にすること ができます/bin/sh
mosvy

1
@mosvy、あなたの言うことはほとんど真実です。しかし、トーバルズはLinuxカーネルについてのみ話すことができます。Chet RameyはbashのPOSIXコンプライアンスを重視し、RedHatはPOSIXコンプライアンスを重視します(Austinグループのスポンサーであり、グループ会議に参加し、多くのGNUソフトウェアを管理しています)。考えは、POSIXがほとんどのUnixライクなシステムが見る唯一の標準であるということです。ユーザーはPOSIX準拠を気にします。これにより、ソフトウェアをさまざまなシステムに移植しやすくなります。理想的ではありませんが、何もないよりはましです。Androidには独自のプログラミングAPIがありますが、POSIXはそれほど心配していません。
ステファンシャゼル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.