シェルスクリプトを記載する時、bashで実行されることを意識する際、冒頭のシェバン (shebang) 行に
1 | #!/bin/bash |
と記載することが多いです。
Linuxではこれで全く問題ないのですが、近年の macOS の状況を見ていると、これはちょっと考え直した方がいいかなと思ってきました。
簡潔に言うと、
1 | #!/usr/bin/env bash |
とするのが汎用性の高い運用になると思います。
以降、丁寧な説明です。
シェバン行とは、Unixライクなシステムでスクリプトファイルの1行目に書く特別な行で、通常 “#!” で始まります。この行は、そのスクリプトを実行するためのインタープリタのパスを指定します。
冒頭に記載した
1 | #!/bin/bash |
は、このスクリプトは /bin/bash を使いなさいと指示しているわけです。
で、macOS でこれのバージョンを見てみます。
1 2 3 | /bin/bash --version GNU bash , version 3.2.57(1)-release (arm64-apple-darwin23) Copyright (C) 2007 Free Software Foundation, Inc. |
とバージョン 3.2.57 であることがわかります。macOSはライセンスの問題で bash のバージョンを 3 よりはあげないことにしていると聞いています。(bash のライセンスはバージョン3までは GPL v2, バージョン4以降は GPL v3 です)
Linuxで現在使われているバージョンを確認してみます。たとえばUbuntu 22.04では
1 2 3 4 5 6 7 | /bin/bash --version GNU bash , バージョン 5.1.16(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2020 Free Software Foundation, Inc. ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http: //gnu .org /licenses/gpl .html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. |
とバージョン5.1.16であることがわかります。
なので、もしスクリプトでバージョン4以降でしか有効でない機能をいれたら、macOSでは不具合が出ます。
実際に最近、そのような経験をしました。連想配列を使ったスクリプトを配布したところ、macOSのデフォルトの bash では実行されずにトラブルが続出しました。
連想配列とは以下のようなものです。
1 2 3 4 | declare -A colors colors[red]= "#FF0000" colors[green]= "#00FF00" colors[blue]= "#0000FF" |
このように連想配列 colors を定義すると
1 | echo "${colors[red]}" |
とすると、#FF0000 が出力されます。
このような機能が使えないわけです。
一方、macOSでは、 Homebrew で bash の最新版を入れることができます。
その場合、パスを指定せずに bash –version を実行すると以下のようになります。
1 2 3 4 5 6 7 | bash --version GNU bash , バージョン 5.2.37(1)-release (aarch64-apple-darwin23.4.0) Copyright (C) 2022 Free Software Foundation, Inc. ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http: //gnu .org /licenses/gpl .html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. |
ちなみに
1 | which bash |
をすると、
1 | /opt/homebrew/bin/bash |
となります。(HomebrewはIntel macでは/usr/local/homebrewに、Apple Siliconは /opt/homebrew に入るようです)
で、パスが /opt/homebrew/bin/ に通っていますので、bash をインタラクティブシェルで実行すると、homebrewのbashが実行されます。
ただ、上記のように macOS はCPUの違いで bash のパスが異なります。より汎用性のある方法はないでしょうか。
そこで出てくるのが env となります。
env コマンドは環境変数 PATH を参照して、指定されたコマンドを探してくれます。そのため、シェバン行を
1 | #!/usr/bin/env bash |
とすることで、以下のような利点が得られます:
- システムの PATH環境変数 に基づいて最適な bash を自動的に選択してくれます
- Intel MacでもApple SiliconのMacでも、Homebrewでインストールしたより新しいバージョンのbashを優先的に使用できます
- システムによってインタープリタの場所が異なる場合でも、柔軟に対応できます
この方法は、bashに限らず他のスクリプト言語でも広く使われている手法です。例えば以下のような感じです。
1 2 3 | #!/usr/bin/env python3 #!/usr/bin/env node #!/usr/bin/env ruby |
このように、envを使用したシェバン行は、異なるプラットフォームやシステム構成での互換性を確保する上で、より汎用的になると感じました。特に、異なるOS環境で作業する可能性が高い場合や、スクリプトを広く配布する場合は、この方法が適切だなと思いました。
以上をまとめると、今後、bashを使ったシェルスクリプトのシェバン行は
1 | #!/usr/bin/env bash |
でいきたいと思いました。
最後に、一括で書き換えられるスクリプトを書きました。こちらのリンクからダウンロードできます(右クリックで名前をつけて保存)。
1 | . /update-shebang .sh check <ディレクトリ> |
で、 #!/bin/bash で始まるスクリプトを探し、
1 | . /update-shebang .sh update <ディレクトリ> |
で #!/usr/bin/env bash に置き換えます。