GNU/Linux >> Linux の 問題 >  >> Linux

#!/bin/sh vs #!/bin/bash 移植性を最大限に高める

解決策 1:

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

<オール>
  • 最も移植性が高い:#!/bin/sh を使用します シバンと使用のみ POSIX 標準で指定されている基本的なシェル構文。これは、ほぼすべての POSIX/unix/linux システムで動作するはずです。 (まあ、本当のレガシー Bourne シェルを持っていた Solaris 10 以前を除いて、/bin/sh のように POSIX より前に準拠していませんでした) .)

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

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

  • 移植性が最も低い:#!/bin/sh を使用します shebang を使用し、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 に慣れている場合は、他の UNIX システム (bsd、macOS など) では見られない多くの非標準拡張機能を持つ GNU コマンドに慣れていることになります。残念ながら、ここには簡単なルールはありません。使用しているコマンドのバリエーションの範囲を知っている必要があります。

    移植性に関して最も厄介なコマンドの 1 つは、最も基本的なコマンドの 1 つです:echo .任意のオプションでいつでも使用できます (例:echo -n または echo -e )、または印刷する文字列にエスケープ (バックスラッシュ) を使用すると、異なるバージョンで異なる処理が行われます。後に改行を入れずに、または文字列にエスケープを含めて文字列を出力したいときはいつでも、 printf を使用してください 代わりに (そしてそれがどのように機能するかを学びましょう -- echo よりも複雑です) は)。 ps コマンドもめちゃくちゃです。

    もう 1 つの一般的な注意事項は、コマンド オプション構文の最近の GNU 風の拡張です。古い (標準) コマンド形式では、コマンドの後にオプション (ダッシュ 1 つ、各オプションは 1 文字) が続き、その後にオプションが続きます。コマンド引数。最近の (そしてしばしば移植性のない) 亜種には長いオプションが含まれています (通常は -- で導入されます) )、引数の後にオプションを指定できるようにし、-- を使用する オプションを引数から分離します。

    解決策 2:

    ./configure で ビルド用に TXR 言語を準備するスクリプトを作成した後、移植性を高めるために次のプロローグを書きました。 #!/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 ${@+"[email protected]"}
    fi
    
    # rest of the script here, executing in upgraded shell
    

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

    (私のスクリプトでは、txr_shell variable は、正確に 2 つの目的で使用されます。まず、スクリプトの出力に情報メッセージの一部として出力されます。次に、SHELL としてインストールされます。 Makefile の変数 、だから make レシピの実行にもこのシェルを使用します。)

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

    Solaris 10 ボックスでは、/usr/xpg4/bin/sh Bash が見つからない場合に開始されます。

    プロローグは test を使用して保守的なシェル方言で書かれています ファイル存在テスト、および ${@+"[email protected]"} いくつかの壊れた古いシェルにケータリングする引数を展開するためのトリック (単純に "[email protected]" になります) POSIX準拠のシェルを使用している場合)

    解決策 3:

    Bourne シェル言語のすべてのバリエーションは、Perl、Python、Ruby、node.js、さらには (ほぼ間違いなく) Tcl などの最新のスクリプト言語と比較して、客観的にひどいものです。少しでも複雑なことをしなければならない場合は、シェル スクリプトの代わりに上記のいずれかを使用した方が、長い目で見れば幸せになります。

    これらの新しい言語よりもシェル言語がまだ持っている唯一の利点は、何か 自分自身を /bin/sh と呼んでいます Unix であると称するあらゆるものに存在することが保証されています。ただし、その何かは POSIX に準拠していない場合もあります。レガシーな独自の Unix の多くは、/bin/sh によって実装された言語をフリーズさせました。 およびデフォルト PATH prior のユーティリティ Unix95 によって要求された変更 (そうです、20 年前の Unix95 であり、現在も続いています)。 Unix95 のセット、または運が良ければ POSIX.1-2001 のツールがディレクトリにある可能性がありますnot デフォルトの PATH (例:/usr/xpg4/bin ) しかし、それらが存在する保証はありません。

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

    したがって:

    どこでも動作する必要があるものを書いている場合 Unix を装ったもの (「configure」スクリプトなど) では、#! /bin/sh を使用する必要があります。 、拡張子を一切使用する必要はありません。現在、私はこの状況で POSIX.1-2001 準拠のシェルを作成しますが、誰かがさびた鉄のサポートを求めた場合、POSIXisms にパッチを当てる準備ができています.

    しかし、そうでない場合は どこでも動作する必要があるものを書いている場合、バシズムを使用する誘惑に駆られた瞬間に、すべてを停止して、代わりにより優れたスクリプト言語で全体を書き直す必要があります。未来のあなたはあなたに感謝します。

    (つまり、 Bash拡張機能を使用するのは適切ですか?最初の注文:決して。 2 番目の順序:Bash の対話型環境を拡張するためだけに。スマートなタブ補完と派手なプロンプトを提供します。)


    Linux
    1. / usr/binと/usr/ local / bin Linuxの場合?

    2. / bin/bashではなく/bin/ shを指すシバンを使用する理由はありますか?

    3. / bin/shが/bin/bashではなく/bin/ dashを指すのはなぜですか?

    1. nologin ユーザーのシェルとしての /bin/false と /sbin/nologin の違い

    2. cmake --version は /usr/bin/cmake を指し、どの cmake は /usr/local/bin を指しますか

    3. /dev/shm/ と /tmp/ はいつ使用する必要がありますか?

    1. Linuxは複数の連続したパスセパレーター(/ home //// username /// file)をどのように処理しますか?

    2. /bin と /usr/bin の違い

    3. chroot が失敗します - コマンド `/bin/bash' を実行できません:そのようなファイルまたはディレクトリはありません