Bourneシェルの終焉まで残り1年（→3年） ～ 新しいシェルへの移行は完了していますか？

## はじめに

30年以上もの長い間 UNIX を支えてきた Bourne シェルも UNIX のサポート終了とともに消え去ろうとしています。みなさん、他のシェルへの移行はお済みでしょうか？ 残り一年、まだ移行が済んでいないという人のために、移行のための簡単なガイドラインと各シェルの特徴をまとめました。

:::note warn

気づいたら2027年1月にまで伸びていました。つまり残り3年です...
（記事の内容は気が向いたら修正します）

[New end date of Extended Support for Oracle Solaris 10 and 11.3](https://blogs.oracle.com/solaris/post/new-end-date-of-extended-support-for-oracle-solaris-10-and-113)
(October 25, 2023)

:::

:::note warn
sh は昔は Bourne シェルのことでしたがそれも過去の話です。今どき「Bourne シェル」を解説している記事や sh のことを Bourne シェルと呼んでいる記事は情報が古い（大学関係に多い）、または古い情報を元にして書かれたか、シェルのことを正しく理解してない不正確な記事なので参考になりません。分かりやすい基準ですね。
:::

関連記事 [シェルとUNIXコマンドの未来 ～ これからの10年で起きるシェルスクリプトの変化 ](https://qiita.com/ko1nksm/items/4907c1c88d478cac0be5)

## 残り1年というのはどういうこと？

Bourne シェルは POSIX に準拠していない古いシェルです。**最初の Bourne シェルは 1979 年に登場し、最終バージョンは 1992 年のものです**。ちゃんとしたデータを持っているわけではありませんが、現在 Bourne シェルを使っている最もシェアが高いと思われる UNIX は Solaris 10 です。ただし **Solaris 10 では 1989 年版の Bourne シェルが使われています**。おっと、安心してください。Solaris 10 でも `/usr/xpg4/bin/sh` に新しいシェル（と言っても ksh88 なので十分古いですが）があるので Solaris 10 は Bourne シェルしか使えないという意味ではありません。Solaris 10 のサポートは何回か延長されてきましたが、また改定されないならば延長サポートは 2025 年の1月で終了します。つまりあと 1 年で Bourne シェルを `/bin/sh` として使う環境がなくなるということです。

なお、**Linux、macOS、BSD 系 Unix、新しめの System V 系 Unix を使っている場合は、すでに Bourne シェルからの移行は完了しています**。特に Linux、macOS、FreeBSD、NetBSD、OpenBSD では過去に Bourne シェルが標準的なシステムで使われたことはありません。これらの OS の利用者はマイナーなオープンソース版の Bourne シェル（[Heirloom Bourne Shell](https://heirloom.sourceforge.net/sh.html)、[Schily Bourne Shell](https://codeberg.org/schilytools/schilytools/)）を独自にビルドしてインストールしていない限り Bourne シェルを使ったことはないでしょう。

## 現状の再確認

Bourne シェルの話をするに当たって、まず現状のシェルの変化の再確認をしましょう。

### 「Bourne シェル vs C シェル」の戦いは歴史の話

かつて、Bourne シェルと C シェルの戦いがありました。主に 1980 年代から 1990 年代で、せいぜい 2000 年代ぐらいまでの話です。元から Unix にあったシェルは Thompson シェルです。そして全く新しいシェルとして Bourne シェルの開発が 1975 年の終わり頃に始まり 1976 年の初め頃に最初のバージョンが完成しました（[参考](https://www.bsdcan.org/2015/schedule/attachments/306_srbBSDCan2015.pdf)）。そして 1979 年 1 月（[参考](https://www.tuhs.org/cgi-bin/utree.pl?file=V7)）に Version 7 Unix とともに世の中に広くリリースされました。一方で元の Unix から分岐した BSD Unix では Thmopson シェルをベースとした新しいシェルの開発が始まり（1978年 の 1BSD には C シェルは含まれず）、1978 年頃に C シェルと名前を変えて 1979 年 5 月の 2BSD の一部としてリリースされました（[参考](https://qr.ae/psEXPH)）。現在の C シェルの立ち位置は、歴史的な意味とアンティークが好きな人が使うシェルです。C シェルは技術的にも劣っており新しくシェルを学ぶ人が使うシェルではありません。

1990 年頃は Unix から派生した多くの Unix が存在していました（参考 [A4用紙40枚で1969年から2022年までのUNIXの歴史「Unix History」を一望してみた - GIGAZINE](https://gigazine.net/news/20221204-unix-history-a4/)）。元の Unix から含まれていた Bourne シェルは、BSD Unix を含む多くの Unix に含まれていましたが、C シェルは BSD Unix の派生もしくは C シェルを組み込んだ Unix にしか存在していませんでした。そのため「Bourne シェルはどこにでもあるが C シェルはそうではない」と言われていました。

- 1990 年頃の話
  - 「Bourne シェル」はどの Unix にも存在していた
  - 「C シェル」は主に BSD Unix に存在した

**「Bourne シェルは移植性が高い（どの環境でも動く）」というのは 1990 年代から 2000 年代頃までの考え方**です。当時はどの Unix にも存在していたからです。また C シェルは対話機能に優れているというのも、この時代までの考え方です。たしかに Bourne シェルよりも C シェルのほうが対話機能に優れていますが、**C シェルよりも現在のシェルの方が対話機能に優れています**。

C シェルは BSD Unix で標準シェルとして使われていましたが、現在のシェルに比べてメリットがないため次第に使われなくなっていきました。**FreeBSD では 13.x まで root ユーザーのデフォルトシェルは csh でしたが、FreeBSD 14.x から FreeBSD sh に変更**されました。他の BSD 系 Unix では sh が用いられており、C シェルに触る機会があるのは FreeBSD が最後だったため、意識して C シェルを利用しようと設定しない限り、今後 C シェルが使われることはないでしょう。

ちなみに「C シェル」は csh の正式な用語（[C-Shell](https://en.wikipedia.org/wiki/C_shell)）ですが、「B シェル」はほぼほぼ日本独自の用語です。おそらく「C シェル」に対して Bourne シェルの頭文字から「B シェル」という愛称で呼ばれるようになったのでしょう。ただし Bourne シェルに対して `bsh` というプログラム名が使われることはありました。同様に「K シェル」や「A シェル」も日本（の特定の企業）が使う愛称です。ただし「Z シェル」（[Z Shell](https://en.wikipedia.org/wiki/Z_shell)）は正式な用語です。

### POSIX 準拠シェルへの移行

sh シェルの仕様は 1992 年に POSIX.2-1992 として標準化されました。このシェルの仕様は一般的に POSIX シェルと呼ばれています。コマンド名は `sh` ですが、POSIX は `sh` のことを Bourne シェルとは呼んでいません。POSIX ドキュメントの PDF を検索しましたが「Bourne」の文字が出てくるのはわずか 5 箇所、いずれも昔のシェルの話をしている文脈で登場します。POSIX シェルは当時の最先端のシェルであった KornShell（ksh88）の仕様がベースになっています。

1990年頃、AT&T が始めた商用 Unix ビジネスのためにライセンス的な制限で **Bourne シェルが BSD Unix で使えなくなりました**。そのため新しく ash（Almquist シェル）が BSD Unix で使われるようになりました。ash や元は Bourne シェルのクローンでしたが POSIX シェルの仕様に準拠するために仕様が変更されていきました。現在の FreeBSD sh や NetBSD sh は ash をベースに独自に機能を追加したものです。OpenBSD では KornShell のパブリックドメイン実装版である pdksh をベースに独自に機能を追加した OpenBSD sh が使われています。

商用 Unix では ksh88 から ksh93 への移行が始まりました。POSIX シェルは KornShell の仕様がベースになっているとはいえ、細かい違いがあったため POSIX に完全に準拠したシェルを目指して開発されたものが ksh93 です。そして皆さんご存知の bash や zsh も元は Bourne シェルのクローンから始まりましたが、ks93 の機能を取り込み POSIX に準拠したシェルへとすぐに変化していきました。Bourne シェルは取り残されました。おそらく Bourne シェルを使っていた商用 Unix は、Bourne シェルを POSIX に準拠に修正するよりも KornShell を使用したほうが良いと考えたからでしょう。

シェルの歴史については「[シェルの歴史 総まとめ（種類と系統図](https://qiita.com/ko1nksm/items/e7f43428352c0b4c78f9)」を参照してください。

### さまざまなシェルが sh を名乗るようになった

かつて sh は Bourne シェルの名前でした。厳密に言えば Unix の最初の sh は Thompson シェルであり、改良された PWB シェルが一部で sh として使われていました。**Bourne シェルよりも前のシェルはシェルスクリプトの機能が少なく変数や関数などの機能はなくパイプ程度の機能しか持っていませんでした**。それだけの機能ではシェルスクリプトとして使えないということで一から作り直されたシェルが Bourne シェルです。1979 年にリリースされた Bourne シェル は sh の名前を乗っ取りました。

その Bourne シェルも Solaris 10 を除いて使われなくなりました。各 Unix/Linux では POSIX に準拠していない Bourne シェルではなく、POSIX に準拠したさまざまなシェルを `/bin/sh` として使用しています。歴史的な互換性上の理由から、現在のシェルの多く（bash、ksh93u+m、NetBSD sh、OpenBSD sh）は、POSIX に準拠したモードを備えており `sh` というコマンド名で実行したとき（または `set -o posix` を実行した時）に、完全に POSIX に準拠します。ただし **「POSIX に準拠する」という言葉の意味は拡張機能を無効にするという意味ではなく、POSIX の仕様と矛盾する動作を変更するという意味**です。各 POSIX シェルは拡張機能を POSIX 準拠モードでも有効にしており、POSIX もこの動作を POSIX 準拠であると認めています。

どの環境でも `sh` が含まれているというのは現在でも同じですが、**`sh` の意味がかつての Bourne シェルを指す意味の言葉から POSIX に準拠したさまざまなシェルに変化**しました。そしてそれぞれの POSIX シェルには完全な互換性はありません。**`sh` コマンドはどの環境でも動きますが、`sh` コマンドで実行されるシェルは環境によって異なる**というのが今のシェルの世界です。

### 「sh は移植性が高い」は昔の考え方

**「sh は移植性が高い」と言っている人は、おそらく sh が Bourne シェルだと思いこんでいるのでしょう**。30 年の時代の変化を考慮していません。さまざまなシェルが sh を名乗るようになったという事実から一つの結論が導き出されます。`sh` = Bourne シェルだった昔は `sh` で動いたシェルスクリプトは、他の `sh`（Bourneシェル）で動く可能性は高いものでしたが、**現在は `sh` = POSIX シェルのどれかなので、特定の環境の `sh` で動いたシェルスクリプトが他の環境でそのまま動く保証はありません**。

- 1970年代
  - sh は Thompson シェル（最初の Unix シェル）だった
- 1980年代 〜 1990 年代中頃
  - sh は Bourne シェルまたは Bourne シェル互換シェルの意味だった
  - **「sh の移植性が高い」はこの頃の考え方。sh = Bourne シェルだから**
- 1990 年代中頃 ～ 2000 年代
  - sh の多くは Bourne シェルから、POSIX シェルへ変化しだした
- 2010 年代
  - Bourne シェルはほぼ使われなくなり、POSIX シェルが主流となった
  - sh ≠ Bourne シェルとなった
- 2020 年代以降
  - **Bourne シェルは完全になくなり、sh は多数の POSIX シェルの別名となった**
  - **sh = 多数の POSIX シェルのどれかなので「sh の移植性は高くない」**


POSIX で標準化された機能しか持たないシェルというものはありません。元々 POSIX シェルは KornShell のサブセットとして作られており、KornShell は最初から拡張機能を持っていました。そして他のシェルは KornShell 互換を目指していたので同じく拡張機能を持っていました。FreeBSD sh や NetBSD sh でさえ、POSIX で標準化されていない拡張機能を持っています。例えば FreeBSD sh や NetBSD sh で使える `local` コマンドは POSIX で標準化されていない拡張機能です。FreeBSD sh や NetBSD sh で動いたからと言って、POSIX に準拠した機能だけを使っているということにはならず、他の環境（例えば dash）で動くとは限りません。

現在 Linux が多く使われているという理由から `sh` で起動するシェルの多くは dash または bash のどちらかでしょう。macOS でも `sh` で起動するシェルは（古いバージョンの）bash です。dash は POSIX で標準化されていない拡張機能が少ないシェルですが、マルチバイト文字に対応していないという意味で完全に POSIX に準拠しているわけではありません。マルチバイト文字に対応しないというのはおそらく dash の方針であるため今後も対応されないと思われます。

現在では `sh` で起動する機能が異なるシェルが複数あるため、`sh` で動くシェルスクリプトを書いても移植性は完全なものにはありません。**`sh` は各環境で標準でインストールされているシェルなので、何もインストールしなくても動くというメリットが有るだけ**です。しかし何も追加のソフトウェアをインストールしないでコンピュータを使うことなんてありませんから、`sh` で動くことにメリットがあるのは不特定の人に配布するシェルスクリプトぐらいです。何もインストールしないで使えるというメリットはほとんどなくなりました。**bash や ksh93 などは（インストールするだけで）どの環境でも動くシェル**なので、bash や ksh93 用にシェルスクリプトを書けば、どの環境でもシェルスクリプトは動きます。

30 年前とは世界が変わりました。Bourne シェルが主流の頃はオープンソースという考え方が普及しておらずメーカーが提供している Unix をそのまま使い、OS 以外は自分たちで開発するという時代でした。POSIX で標準化される前は別のシェルを使うためには C 言語コンパイラなどそれなりのツールが必要でインストールは難しい作業でしたが、現在はパッケージ管理システムからインストールするだけです。追加のソフトウェアは一般的に Unix 標準コマンドよりも機能もパフォーマンスも優れています。`/bin/sh` が移植性が高いというのは sh = Bourne シェルだった時代の考え方です。POSIX で OS のインターフェースが標準化された結果、**POSIX に準拠してどの環境でも動くソフトウェアは大きく増えました。その一つが bash です**。`sh` で動かすシェルスクリプトは多数のシェルを考慮しなければならなくなりましたが、`bash` で動かすシェルスクリプトは bash で動くことだけを考えれば十分です。bash はどの環境でも動かすことができるので、bash スクリプトの移植性は高いということです。

## 移行先シェルについての評価

ここでの評価の基準は、**Bourne シェルからの移行先としてシェルスクリプトを書くためのシェル**です。例えば macOS でのデフォルトの対話シェルである zsh はデフォルトで POSIX シェルとは異なる動作をしていたり、zsh でシェルスクリプトを書く場合は少ないだろうという理由から省いています。zsh が劣っているという意味ではなく対話シェルとして使う場合には非常に良いシェルです。ここに挙げているものはすべて POSIX シェルですが、他にも mksh、yash、zsh など POSIX に準拠しているシェルはあります。

| シェル           | 汎用性    | 機能性    | 移行先のシステム                            |
| ---------------- | -------- | --------- | ------------------------------------------- |
| bash             | **高い** | **高機能** | Linux、macOS、BSD 系 Unix、System V 系 Unix |
| ksh93 / ksh93u+m | **高い** | **高機能** | Linux、macOS、BSD 系 Unix、System V 系 Unix |
| dash             | 普通     | 低機能     | Linux、macOS、BSD 系 Unix、System V 系 Unix |
| FreeBSD sh       | 低い     | 低機能     | 基本的に FreeBSD のみ                       |
| NetBSD sh        | 低い     | 低機能     | 基本的に NetBSD のみ                        |
| OpenBSD sh       | 低い     | 低機能     | 基本的に OpenBSD のみ                       |

この表の汎用性とは多くの環境で動くということです。bash や ksh93 などは多くの OS で動作します（標準的なパッケージ管理システムから簡単にインストールできます）。FreeBSD sh や NetBSD sh は、OS の一部として作られているため（動く可能性はありますが）原則的にその OS にしか対応していません。POSIX で標準化されている最低限の機能しか使わなければ概ね動くとは思いますが、結局のところ例えば FreeBSD sh で動いていたシェルスクリプトを Linux で動かそうとするならば、違うシェルで動かすことになるのでテストが必要になります。

### bash

エンドユーザー、開発者ともに利用者が多く、十分で継続的なメンテナンスと高い互換性を実現しており、対話シェルとしてもシェルスクリプトとしても使える高機能な万能シェルです。

Linux、macOS、BSD 系 Unix、System V 系 Unix と、どの環境でも動くシェルなので bash 用としてシェルスクリプトを書くと非常に移植性が高いシェルです。全てではありませんが Linux にほぼインストールされており、Red Hat 系 Linux ではシステムシエル `/bin/sh` としても使われています。

**POSIX に厳密に準拠する POSIX モードに加えて、過去の bash のバージョンとの互換性を保つ機能（`BASH_COMPAT` 参照）を持っている**ので、ビジネス用途として最も適したシェルです。

### ksh93 / ksh93u+m

いくつかの商用 Unix では Bourne シェルの後継として主に ksh が `/bin/sh` として使われてきました。ksh は元々クローズドソースでしたが、後に ksh93 がオープンソースとなり Linux や BSD 系 Unix など幅広い環境で動作するようになりました。2012 年の ksh93u+ からしばらく開発が行われていませんでしたが、2022 年に新たに ksh93u+m として開発が再開しており、最新の ksh93 として使えます。

ksh93u+m は ksh93 のソースコードを元に開発されており、商用 Unix で ksh93 を使っている場合の移行先のシェルとしても使えます。つまり**現在 ksh93 を使っている場合は、必ずしも bash 用に書き換える必要はない**ということです。ただし、bash よりもユーザー数、開発者数は少ないため、将来のことを考えて bash に乗り換えるという判断は間違ったものではないと思います。bash は ksh93 の機能を取り入れているため基本的な機能は bash と互換性があります。ただし bash と ksh93 では ksh93 の方が高機能であるため、ksh93 の高度な機能を使っている場合には、将来的に移行するならば ksh93u+m の方が良いでしょう。

### dash

dash は、Debian/Ubuntu 系 Linux で `/bin/sh` として使われているシェルです。元は BSD Unix の ash と呼ばれていたシェルの後継シェルにあたり、拡張機能が少なく POSIX シェルに近いシェルです。dash は debian ash の略で、その名の通り Debian 用のシェルですが、多くの環境に移植されており、macOS や BSD 系・System V 系 Unix でも使うこともできます。マルチバイト文字に対応していないため完全に POSIX に準拠しているわけではありません。

### FreeBSD sh

FreeBSD sh は、FreeBSD で `/bin/sh` として使われているシェルです。元は BSD Unix で ash と呼ばれていたシェルの後継シェルにあたり、拡張機能が少なく POSIX シェルに近いシェルです。FreeBSD の一部として開発が行われており、基本的に FreeBSD でしか使えません。FreeBSD を使用する場合の選択肢の一つです。

余談ですが、FreeBSD の root のデフォルトシェルは `/bin/csh` が使われてきましたが、FreeBSD 14 から `/bin/sh` に変更されました（`/bin/csh` も使えます）

### NetBSD sh

NetBSD sh は、NetBSD で `/bin/sh` として使われているシェルです。元は BSD Unix で ash と呼ばれていたシェルの後継シェルにあたり、拡張機能が少なく POSIX シェルに近いシェルです。NetBSD の一部として開発が行われており、基本的に NetBSD でしか使えません。NetBSD を使用する場合の選択肢の一つです。

### OpenBSD sh

OpenBSD sh は、OpenBSD で `/bin/sh` として使われているシェルです。元は pdksh と呼ばれる ksh88 をベースにパブリックドメインで再実装されたシェルの後継シェルにあたります。ksh というコマンド名で起動できますが、AT&T 本家の ksh88 ではないので注意してください。配列などを使うこともできるのですが、ksh88 ベースの古い配列であるため bash や ksh93 と互換性がありません。OpenBSD の一部として開発が行われており、基本的に OpenBSD でしか使えません。OpenBSD を使用する場合の選択肢の一つです。

## Bourne シェルの終焉で嬉しいこと

Bourne シェルがなくなるということは、もはや Bourne シェルを考慮してシェルスクリプトを書く必要がなくなったということです。Bourne シェルで使えないからといって新しい機能の使用を諦める必要はなく、最低ラインを POSIX シェルにすることが可能です。以下に嬉しいことを挙げていますが、これらはごく一部です。Bourne シェルと POSIX シェルの違いについては以下を参照してください。

- [Bourne Shell(古いsh)とPOSIXシェル(今のsh, bash等)の違い](https://qiita.com/ko1nksm/items/6b61b0e687d206be9cb0)

### 数値計算に `expr` コマンドを使わなくて良くなった

かつて Bourne シェル では数値計算に `expr` コマンドを使わなければなりませんでしたが、Bourne シェルの終焉でその必要はなくなりました。`expr` コマンド相当の数値計算機能は POSIX シェルで標準化され組み込まれています。`sh` では `expr` コマンドを使わなければいけないというのは過去の話です。

### 全てのシェルで `test` / `[ ... ]` がシェルに組み込まれた

過去には `test` / `[ ... ]` が外部コマンドであったため、パフォーマンスをあげるために `case` で代用していた事がありました。現在はすべての POSIX シェルでシェルに組み込まれたため、文字列の比較で `case` を使う必要はありません。

### 簡単な文字列処理がシェルの変数展開で行えるようになった

Bourne シェルには文字列処理を行う方法は、文字列結合と `IFS` を使った単語分割以外ありませんでした。そのため `sed` などの外部コマンドに頼らずを得ませんでした。現在は文字列処理はシェル自身で行えるため、簡単な文字列処理に外部コマンドを使う必要はありませんし、パフォーマンスも向上しました。POSIX で標準化されどの環境でも使える変数展開には次のようなものがあります。

```sh
${var#pattern}   # 変数varの前方からパターンpatternに一致（最小一致）するものを取り除く
${var##pattern}  # 変数varの前方からパターンpatternに一致（最大一致）するものを取り除く
${var%pattern}   # 変数varの後方からパターンpatternに一致（最小一致）するものを取り除く
${var%%pattern}  # 変数varの後方からパターンpatternに一致（最大一致）するものを取り除く
```

### OS に依存しないシェルが多数派になった

昔はシェルは OS に組み込まれており、OS 標準シェル以外を使うことはあまり考えられていませんでした。それら OS に組み込まれているシェルは他の OS では動かず、そのため POSIX でシェルが標準化され、OS が違ってもだいたい同じような動きをするようになってきました。しかし現在は bash、ksh93u+m、dash など、特定の OS に依存しないシェルが普及しました。OS 専用のシェルとは異なり OS に依存しないシェルはどの環境でも動きます。オープンソースとなり誰でも自由に使うことが出来ます。OS 標準のシェルを使わずとも追加でシェルを簡単にインストールして使えることが当たり前となりました。

### Bourne シェル用の書き方をしなくて良くなった

かつては `sh` では Bourne シェルに対応した書き方が必要でしたが、それが必要なくなりました。Bourne シェル用の書き方とは次のようなものです。

```sh:Bourne シェル用の古い書き方（現在は不要）
VAR=123; export VAR
# export VAR=123 は POSIX シェル以降の書き方
# 注意 コマンド置換と組み合わせて使う場合は一旦変数に入れないとエラーを検出できないので
# export VAR=$(cmd) という書き方は推奨しない（変数への代入とexportは分けて書く）

VAR=`cmd` # コマンド置換をネストした場合に読みにくくなる
# VAR=$(cmd) が POSIX シェル以降の推奨される書き方
```

### 古いシェルのバグの回避策が必要なくなった

古いシェルにはバグがいくつもありました。今もまったくないというわけではありませんが、その数は大きく減っています。過去には特定のシェルのバグを回避するために、一見見ただけではどのような意味があるかわからない回避策が用いられていました。例えば次のようなものです。

```sh:古いシェルのための回避策（現在は不要）
[ x"$str" = xfoo ]
# 現在は素直に [ "$str" = foo ] とかけば良い
# 昔は str に -z や ! などが入ると演算子として扱われるバグがあった

cmd ${1+"$@"}
# 現在は cmd "$@" とかけば良い
# 昔は位置パラメータがないときに　"$@" が "" （一つの空文字）として扱われていた
```

## 移行時の注意点

### `/bin/sh` だからといって全て同じ動きをするわけではない

sh は環境ごとに実装が異なり、動作に違いがあります。例えば FreeBSD sh と NetBSD sh はどちらも ash ベースの拡張機能が少ないシェルですが、この 2 つ間でさえ動作の違いや拡張機能の違いがあります。POSIX は拡張機能の実装を禁止していませんし、POSIX で標準化されている範囲であっても動作の違いを許容している部分があります。いくつか例を上げましょう。これらは FreeBSD sh や NetBSD sh では動くが、他のシェルでは動かないコード、FreeBSD sh と NetBSD sh の違いの例です。

```sh
VAR1=123 VAR2=456
VAR1=$VAR2 VAR2=$VAR1
echo "$VAR1 $VAR2" # => 「456 123」と出力されるのは FreeBSD sh と NetBSD sh だけ

# FreeBSD sh と NetBSD sh は then と else の間を省略できる
if false; then
    # POSIXシェルはここを省略できない
else
  echo "false"
fi

# FreeBSD sh では == に対応しているが、NetBSD sh は非対応（NetBSD 10 時点）
if [ foo == foo ]; then
  echo "true"
fi

v="foo bar"
export V=$v # readonly や local でも同様
echo "$V" # FreeBSD shは「foo bar」、NetBSDは sh「foo」
```

他にも、`local` コマンド、`builtin` コマンド、`jobid` コマンド、`setvar` コマンドなど、POSIX で標準化されていないコマンドも使えますし、他のシェルで動作が異なる部分があります。

```sh:ksh と zsh で動作が異なる部分
total=0
seq 10 | while IFS= read -r i; do
  total=$((total + i))
done
echo "$total" # => 0 （kshとzshでは55と出力される）
```

簡単に言うならば、`/bin/sh` で動くシェルスクリプトを書くのであれば、対応しなければいけない環境の `/bin/sh` （複数の種類のシェル）で動くことをしっかりテストする必要があるということです。逆に特定の環境に OS に依存しない bash や ksh をインストールすれば、それらのシェルで動くことだけをテストすれば良いということです。

もっともシェルスクリプトに移植性を持たせたい場合は、シェルだけではなく利用している外部コマンドの動作の違いにも対応する必要があるので、シェルだけをインストールすれば問題が解決するというわけではありませんが、少なくともシェルの問題は解決します。

### POSIX シェルは Bourne シェルの完全上位互換シェルではない

POSIX シェルは Bourne シェルの完全上位互換シェルではありません。Bourne シェルで正しく動いていたシェルスクリプトが、POSIX シェルで同じように動くとは限りません。POSIX シェルは Bourne シェルに機能を追加しただけではなく一部の動作が変わっているので互換性がない部分があります。例えば次の例は Bourne シェルから POSIX シェルに移行すると動かなくなる例です。

```sh:Bourne シェルで動いていたコードが POSIX シェルで動かなくなる例
#!/bin/sh

i=123

# サブシェルが使われるので i はローカル変数相当
while read i; do
  echo "$i"
done < file.txt

echo "$i" # => Bourne シェルでは 123、POSIX シェルでは通常は空文字
```

POSIX シェルは Bourne シェルの完全な上位互換シェルではないので移行した際にはテストが必要です。

## さいごに

もうそろそろ Bourne シェルのことは忘れましょう。