今回は、「/bin/sh」というパスで配置されているシェルについてちゃんと把握しておこう、という話だ。LinuxとMacの違いに限った話ではなく、異なるLinuxディストリビューションを使う場合も当てはまるのだが、この部分を曖昧にしておくと「コピーしたシェルスクリプトがなぜか実行できない」といった事態が発生して困ることになる。きちんと理解しておいて損はない。

/bin/shの実態は同じじゃない

Linuxでシェルスクリプトと言うと「/bin/sh」が使われていることが多い。この「/bin/sh」、見た目は同じでもその実態は、実は同じものとは限らなかったりする。例えばWSLのUbuntuでは次のようになっている。

  • Ubuntu / WSL

    Ubuntu / WSL

パス 実態
/bin/bash bash
/bin/dash dash
/bin/sh -> /bin/dash dash

最近のUbuntuでは/bin/shは/bin/dashへのシンボリックリンクになっている。要するに/bin/shの実態は「dash」と呼ばれるプログラムなのだ。

一方、macOS Montereyは次のようになっている。

実態が見えにくいが、実行してみると内容を確認できる。

表に整理すると次のようになる。

パス 実態
/bin/bash bash
/bin/dash dash
/bin/sh exec(/private/var/select/sh) /private/var/select/sh -> bash (dash, zsh)

macOS Montereyは少々わかりにくいのだが、/bin/shというプログラムは、/private/var/select/shで指定されているシンボリックリンクを実行するというプログラムになっている。上記スクリーンショットでは/private/var/select/shは/bin/bashへのシンボリックリンクになっており、つまりMac Montereyでは/bin/shの実態はbashということになる。macOS Montereyでややこしいのは、場合によっては/bin/shの実態はdashだったりzshだったりする可能性もあるということだ。

こんな感じでLinuxディストリビューションやMacでは/bin/shの実態が全て同じという状況にはなっていない。/bin/shの実装系はそれぞれ異なっており(そして同じディストリビューションでもバージョンが異なると/bin/shが違ってることもあり)、注意が必要だ。

/bin/shとしての/bin/bash

Linuxディストリビューションのいくつかは/bin/shの実態としてbashを使っている。bashはBourne Shell系の多機能インタラクティブシェルだ。ユーザーがインタラクティブに使うための機能追加や、プログラミング言語的な機能を実現するための機能が追加されており、Bourne Shellと比べてかなり多くの機能が導入されている。多くのLinuxディストリビューションがbashをデフォルトのインタラクティブシェルに採用しているほか、/bin/shの実態としても使用している。

bashと同世代で似たようなシェルにはzshがある。zshもBourne Shell系の多機能インタラクティブシェルで、位置付けはbashと似ている。追加された拡張機能はbashと互換性がないものが多いが、実現している機能は似ている。macOS Montereyではzshがデフォルトのインタラクティブシェルに採用されている。

bashもzshもよく似ているが、大きな違いはライセンスの部分だ。現在のbashはGPL3で、zshはBSDライセンスやMITライセンスなど、似たような緩いライセンスを採用している。アプライアンスや組み込み機器においては、GPL3のソースコードを使ったソフトウエアの採用を懸念するベンダーは少なくない。そんな場合はbashではなくzshのほうが選択肢になるわけだ。AppleがmacOSのシェルをzshに変更したのも、ライセンスが主な理由ではないかと見られている。

ここで注意が必要なのは、bash専用の機能を使って記述されたシェルスクリプトはzshでは実行できないし、逆にzsh専用の機能を使って記述されたシェルスクリプトはbashでは実行できないことだ。そして同じように、bashやzsh専用の機能を使って記述されたシェルスクリプトは、Bourne Shellのような源流となるシェルでも実行することができない。

/bin/shとしての/bin/dash

Bourne Shell系のシェルであり、最もオリジナルのBourne Shellに近い実装系のシェルがashだ。開発者であるKenneth Almquist氏の名前からAlmquist Shell(アルムクィストシェル)とも呼ばれている。NetBSDやFreeBSDではashが/bin/shとして使われている。いちばんオリジナルに近く、Bourne Shell互換の高いシェルということになる。

このashをLinuxに移植したシェルがdashだ。特徴はashとほとんど同じだ。Bourne Shellに最も近いシェル実装系の一つであり、軽量で高速という特徴を持つ。bashよりも高速に動作するので、最近ではシステムデフォルトのシェルスクリプトを実行するシェルとしてはbashではなくdashを採用する傾向が見られる。

このように、/bin/shの実装系としてはbash、またはdash(ash)という2つが主に存在していることになる。当然ながら、bashの拡張機能を使ったシェルスクリプトはdashでは実行することができない。

bashかdashか把握しておこう

/bin/shの実装系はbashだったりdashだったりashだったりとさまざまだ。環境によっては/bin/shで書いていたシェルスクリプトが、実際にはbashを使ったシェルスクリプトになっていた、ということはよくある。

ここで問題になるのは、/bin/shがbashだと知らないでほかのOSやディストリビューションに持っていったときに起こる。実装系がashやdashだった場合、bashを前提に書かれたシェルスクリプトは実行することができない。ashやdashで動作するように書き換えるか、明示的にbashを使うように書き換える必要がある。

動作しないことを理解しているなら問題の解決は簡単だが、状況を理解していないとかなり悩む可能性がある。LinuxなどのUNIX系OSでは、同じコマンド名でも実装系が異なるケースがあるというのはこれまで取り上げてきた通りだ。/bin/shも同じであり、異なるプラットフォームを使っている場合にはその辺りを把握しておくとよいだろう。