web-dev-qa-db-ja.com

移植性を最大化するための#!/ bin / shと#!/ bin / bash

私は通常、Ubuntu LTSサーバーを使用しています。これは、私が理解しているものからsymlink /bin/sh/bin/dash。 symlinkを介した他の多くのディストリビューション/bin/sh/bin/bash

それから、スクリプトが#!/bin/sh上のすべてのサーバーで同じように実行されない可能性がありますか?

サーバー間でのスクリプトの移植性を最大限にしたい場合に、シェルがスクリプトに使用する推奨の方法はありますか?

37
cherouvim

シェルスクリプトの移植性には、およそ4つのレベルがあります(Shebangの行に関する限り)。

  1. 最も移植性が高い:#!/bin/shシバンと POSIX標準 で指定されている基本的なシェル構文onlyを使用します。これは、ほとんどすべてのPOSIX/unix/linuxシステムで機能するはずです。 (まあ、実際のレガシーBourne ShellがあったSolaris 10以前のバージョンを除いて、POSIXが準拠していないため、/bin/sh。)

  2. 2番目に移植性が高い:#!/bin/bash(または#!/usr/bin/env bash)シバン行、およびbash v3機能に固執します。これは、(予想される場所に)bashがあるすべてのシステムで機能します。

  3. 3番目に移植性が高い:#!/bin/bash(または#!/usr/bin/env bash)シバン行、およびbash v4機能を使用します。これは、bash v3(たとえば、ライセンス上の理由で使用する必要があるmacOS)を備えたシステムでは失敗します。

  4. 移植性が最も低い:#!/bin/shシバンし、POSIXシェル構文にbash拡張を使用します。これは、/ bin/shのbash以外のもの(最近のUbuntuバージョンなど)があるシステムでは失敗します。これを行わないでください。それは単に互換性の問題ではなく、単なる間違いです。残念ながら、それは多くの人が犯す間違いです。

私の推奨事項:スクリプトに必要なすべてのシェル機能を提供する最初の3つのうち最も保守的なものを使用してください。移植性を最大にするために、オプション#1を使用しますが、私の経験では、いくつかのbash機能(配列など)は、#2で十分に役立ちます。

あなたができる最悪のことは#4、間違ったシバンを使うことです。基本的なPOSIXの機能とbashの拡張機能がわからない場合は、bashのシバン(つまり、オプション#2)を使用するか、非常に基本的なシェル(Ubuntu LTSサーバーのダッシュなど)でスクリプトを完全にテストします。 Ubuntu wikiには 注意すべきバシズムの良いリスト があります。

UnixとLinuxの質問 "sh互換とはどういう意味ですか?" とStackoverflow質問 "shとbashの違い"

また、異なるシステム間で異なるのはシェルだけではないことに注意してください。 Linuxに慣れている場合は、GNUコマンドに慣れています。これらのコマンドには、他のUNIXシステム(bsd、macOSなど)にはない非標準の拡張機能がたくさんあります)。残念ながら、ここに単純なルールはありません。使用しているコマンドのバリエーションの範囲を知っているだけです。

移植性の点で最も厄介なコマンドの1つは、最も基本的なものの1つです:echo。オプションを使用していつでも(例:echo -nまたはecho -e)、または出力する文字列にエスケープ(バックスラッシュ)を使用すると、バージョンによって動作が異なります。改行なしで文字列を印刷する場合、または文字列にエスケープを使用する場合は、代わりにprintfを使用してください(そして、その機能を学びます-echoよりも複雑です)。 psコマンドは これも混乱 です。

もう1つの一般的な注意事項は、コマンドオプションの構文に対する最近/ GNUの拡張です。古い(標準)コマンド形式は、コマンドの後にオプションが続く(ダッシュが1つ、各オプションが1つの文字である)ことです。コマンド引数。最近の(多くの場合、移植できない)バリアントには長いオプションが含まれています(通常、--)、引数の後にオプションを置くことを許可し、--を使用して、オプションと引数を区切ります。

66
Gordon Davisson

TXR言語を作成する準備をする./configureスクリプトで、移植性を高めるために次のプロローグを書きました。スクリプトはbootstrap自体、#!/bin/shがPOSIXに準拠していない古いBourne Shellであっても、それ自体です(私はSolaris 10 VMですべてのリリースをビルドしています)。

#!/bin/sh

# use your own variable name instead of txr_Shell;
# adjust to taste: search for your favorite shells

if test x$txr_Shell = x ; then
  for Shell in /bin/bash /usr/bin/bash /usr/xpg4/bin/sh ; do
    if test -x $Shell ; then
       txr_Shell=$Shell
       break
    fi
  done
  if test x$txr_Shell = x ; then
    echo "No known POSIX Shell found: falling back on /bin/sh, which may not work"
    txr_Shell=/bin/sh
  fi
  export txr_Shell
  exec $txr_Shell $0 ${@+"$@"}
fi

# rest of the script here, executing in upgraded Shell

ここでの考え方は、実行中のシェルよりも優れたシェルを見つけ、そのシェルを使用してスクリプトを再実行することです。 txr_Shell環境変数が設定されているため、再実行されたスクリプトは、それが再実行された再帰インスタンスであることを認識します。

(私のスクリプトでは、txr_Shell変数も2つの目的で使用されています。1つ目は、スクリプトの出力に情報メッセージの一部として出力されます。2つ目は、Shell変数としてMakefile。これにより、makeはレシピの実行にもこのシェルを使用します。)

/bin/shがダッシュであるシステムでは、上記のロジックが/bin/bashを検出し、それを使用してスクリプトを再実行することがわかります。

Solaris 10ボックスでは、Bashが見つからない場合、/usr/xpg4/bin/shが作動します。

プロローグは保守的なシェル方言で書かれており、ファイル存在テストにtestを使用し、一部の壊れた古いシェル(POSIX準拠のシェルにいる場合は単に${@+"$@"}になる)に対応する引数を拡張する"$@"トリックを使用します。 。

5
Kaz

Bourne Shell言語のすべてのバリエーションは、Perl、Python、Ruby、node.js、さらには(おそらく)Tclのような最新のスクリプト言語と比較して、客観的にひどいものです。少しでも複雑なことをする必要がある場合、シェルスクリプトの代わりに上記のいずれかを使用すると、長期的には幸せになります。

Shell言語がこれらの新しい言語よりも優れている唯一の利点は、something自体を呼び出す_/bin/sh_が、Unixであると主張するすべての場所に存在することが保証されていることです。ただし、POSIXに準拠していないものもあります。レガシーの独自仕様のUnixの多くは、_/bin/sh_によって実装された言語と、UNIX95が要求する変更(はい、Unix95、20年前)のデフォルトPATHpriorのユーティリティを凍結しましたそして、カウント)。一連のUnix95、または運が良ければPOSIX.1-2001さえあるかもしれませんが、デフォルトのPATH(例:_/usr/xpg4/bin_)上のディレクトリにないしかし、それらが存在することは保証されていません。

ただし、Perlの基本は、Bashよりも任意に選択されたUnixインストールに存在する可能性が高い。 (「Perlの基本」とは、_/usr/bin/Perl_が存在し、someであり、かなり古いバージョンのPerl 5であることを意味します。そのバージョンのインタープリターに同梱されているモジュールも使用できます。)

したがって:

動作しなければならない何かを書いている場合everywhere Unixであると主張するもの( "configure"スクリプトなど)は、_#! /bin/sh_、そして拡張機能を一切使用する必要はありません。現在、私はこの状況でPOSIX.1-2001準拠のシェルを作成しますが、誰かがさびた鉄のサポートを求めた場合、POSIXismsにパッチを適用する準備をします。

しかし、あらゆる場所で機能する必要があるものを記述していない場合、Bashismをまったく使用したくなったときは、停止して、より良いスクリプトですべてを書き直してください。代わりに言語。あなたの将来の自己はあなたに感謝します。

(つまり、isの場合、Bash拡張機能を使用するのが適切ですか?最初の順序:絶対にしないでください。2番目の順序:Bashインタラクティブ環境を拡張するだけです。プロンプト。)

2
zwol