Unix/Linux シェル考古学 ～シェルスクリプトが本物のプログラミング言語である理由～

## はじめに

タイトルは「[Unix考古学](https://www.amazon.co.jp/dp/B01N6NML10)」のパクリですがこの記事と直接の関係はありません。Unix 誕生時点のシェルから POSIX シェルが策定されるまでのシェルの歴史を調べてみました。サブタイトルが微妙にタイトルとずれてる感じがするかもしれませんが、この記事はもともとシェルスクリプトは何を目指して設計された言語なのか疑問になって調べ始めたものだからです。時々「シェルスクリプトじゃなくて本物のプログラミング言語で書くべきだ」というような話を目にしますが、実際はシェルスクリプトはプログラミング言語なのか？そうではないのか？なので、むしろサブタイトルのほうが本題です。

ソースは wikipedia（日本語版は情報が古いので主に英語版）およびそこからの外部リンクなどを適当に調べてまとめたものです。内容に疑問に感じる場合は独自で調べるのをおすすめします。ちなみに [~sven_mascheck/](https://www.in-ulm.de/~mascheck/) はおすすめです。この記事とは比べ物ならない圧倒的な情報量を誇っています（例えば [#! の由来](https://www.in-ulm.de/~mascheck/various/shebang/)とか）。PWB シェルに関しては「Unix 考古学」も参照しました。また私の興味の対象が POSIX シェル、およびその元となった KornShell とその直系の先祖である Bourne シェルなので C シェルやその他のシェルについては最低限しか調べていません。気が向いたら追記するかもしれません。もし間違いなどがあれば教えていただけると助かります。

<details>
<summary>**2021-06-04 追記 この記事を書いた経緯とか結論に興味がある人へ**</summary>
<div>
どうもこの記事を書いた経緯とか結論に興味がある人がいるようなので少し説明を追記します。上では省略しましたがこの記事を書き始めた直接のきっかけは[別件](https://qiita.com/ko1nksm/items/efb9693a79412f445a83)でたまたま[このファイル](https://github.com/modernish/modernish/blob/master/share/doc/modernish/DESIGN.md)に次のような書かれてあるのを見つけたからです。

> The shell is an actual programming language. That's how it was designed and advertised right from the early days of the Bourne shell. Unfortunately, few take it seriously as such.

（シェルは実際にプログラミング言語です。Bourne シェルの最初からそのように設計され宣伝されてきました。残念なことにそれを本気にする人はほとんどいません。）

「残念なことにそれを本気にする人はほとんどいません」っていうのは私も同じ思いですが、それはそれとして気になったのは「Bourne シェルの最初からそのように設計され宣伝されてきました」という所です。シェルスクリプトがプログラミング言語であるというのは、使っていればわかることなのですが、そのように設計され宣伝されてきたというからにはその証拠が存在しているはずです。

このドキュメントを書いた Martijn Dekker さんが単にシェルスクリプトが好きなだけの人というのなら私も気には止めなかったと思いますが、この方は現在 POSIX シェルの一つである KornShell (ksh) 最新版（の最有力候補）の [ksh 93u+m](https://github.com/ksh93/ksh) のメンテナンスをされている方です。オリジナルの開発者ではありませんがシェルを正しく理解している開発者の一人と言えるでしょう。単に（膨大な数の）既存のパッチを適用しているだけというわけではなく、そのコードの意味を完全に理解した上で（当たり前なんですが他人が書いたコードを理解する大変さはわかりますよね？）修正を行うと[公言](https://github.com/ksh93/ksh) されており独自の修正も行っています。またその修正内容を後のメンテナのためにコミットメッセージに完全に文書化する（[例](https://github.com/ksh93/ksh/commit/e939860a86e1c9f1e7eb0a971d4bc2c5336efc9d)）というストリクトなやり方でメンテナンスされています。そのような方が具体的な根拠もなくプログラミング言語として設計され宣伝されてきたと言うとは思えません。最初は簡単に見つからないだろうと思って聞いてみようかと思ったのですが、その前に自分で調べるべきだなと思って調べたら意外と簡単に見つかったという流れで記事としてまとめることにしました。

なので私としてはこのドキュメント内容が事実であることの調査・検証を行っただけなので「本物のプログラミング言語だったよ」という結論でこの記事の話は終わりなのです。

「"本物のプログラミング言語"だからなに？」とその先が聞きたくてしょうがない人のために答えると、ようするにシェルスクリプトには一般的なプログラミング言語の技術が応用できるということです。もちろんシェルスクリプトがどんなことにも使える汎用のプログラミング言語とは思ってはいませんが Unix 環境（つまり外部コマンドの呼び出し）に特化しているプログラミング言語だとは思っています。シェルスクリプト特有の技術（パイプで繋ぐとか）があるのはその通りですが、それと同時にプログラミング言語として設計されているのであれば一般的なプログラミング言語の技術も応用できるだろうという仮説がなりたちます。そして実際にシェルスクリプトで一般的なプログラミング言語の技術を応用することができました。現在その技術を広めるために執筆中（色々あって遅れてます・・・）ですが、まずその前にシェルスクリプトが本物のプログラミング言語であるということを伝えることにしました。

シェルスクリプトが本物のプログラミング言語であると前提に立つことで見えてくる技術（といっても他の言語の技術の応用に過ぎないのですが・・・）は近いうちに公開しますので「本物のプログラミング言語だから」の続きが気になってしょうがない方はしばらく楽しみにしてお待ち下さい。
</div>
</details>

## 用語の確認

いきなりですが問題です。Unix（商用 UNIX を含む正統な系統）の歴史においてシステムシェル (`/bin/sh`) はなんという名前のシェルでしょうか？

**答え** Thompson シェル → PWB シェル → Bourne シェル → POSIX シェル (ksh）と変化しています。（[参考](https://www.in-ulm.de/~mascheck/various/shells/)）

Unix のシステムシェルのパスは伝統的に `/bin/sh` が使われてきましたが常に互換性が保たれていたわけではありません。PWB シェル は Thompson シェル の上位互換シェルであり、POSIX シェルは Bourne シェル の上位互換シェルですが、前 2 つと後ろ 2 つの間には互換性がありません。

Bourne シェルとはスティーブン・ボーンが開発したシェルの事を指します。勘違いされやすい（？）ですが **bash は Bourne シェルではありません。**bash は Bourne シェルの上位互換であるため「Bourne シェル（Bシェル）**系**」とか互換シェルと呼ぶなら正しいのですが Bourne シェルそのものではありません。また dash や bash など現在の主流のシェルは「POSIX に準拠したシェルかつ Bourne シェル**系**」ですが **Bourne シェルそのものは POSIX に準拠していません。**ややこしくなるので Bourne シェルという名前は過去のシェルの名前として使い、現在の POSIX に準拠したシェルは「POSIX シェル」と呼ぶことをおすすめします。

**補足** 実は POSIX では[システムシェルのパスは `/bin/sh` とは規定されていません](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html#tag_20_117_16)。

> Applications should note that the standard PATH to the shell cannot be assumed to be either /bin/sh or /usr/bin/sh, and should be determined by interrogation of the PATH returned by getconf PATH, ensuring that the returned pathname is an absolute pathname and not a shell built-in.

**補足2** B シェルと `bsh` について

公式に C シェル（C Shell）を名乗っている csh とは違って B シェルという呼び方は日本独自なんじゃないか？と疑ってたりもしますが少なくとも `/bin/bsh` というパスは過去（？）に使われており、一般的に `/bin/bsh` の実体は Bourne シェルとなっているようです（[参考](https://www.in-ulm.de/~mascheck/various/shells/)）。しかし検索してみると `/bin/bsh` は `/bin/ash` へのシンボリックリンクとなっている環境もあったようです。これは初期の ash が Bourne シェルのクローンだったからだと思われます（現在の ash は POSIX シェルなので Bourne シェルとは完全な互換性がない）。

C シェルと言った時、それが csh だけの事を指していても tcsh を含めた C シェル系を指していてもあまり大差ないのですが、B シェルと言った時 Bourne シェルだけの事を差しているのか？それとも Bourne シェル系の意味で使ってるのかよくわかりません。bash を指して B シェルと言うならそれは B シェル系という意味であり ash、ksh、zsh も B シェルになってしまいますし もちろん POSIX に準拠してない古い Bourne シェルも B シェル系に含まれます。はたして本当にそういう意味で使ってるのかどうか。ややこしいので B シェルも bsh も使うのをやめて、正式な Bourne シェルや bash などの具体的なシェル名を言うか POSIX シェルと言ったほうが良いでしょう。

## シェルスクリプトがプログラミング言語である理由

PWB シェルの開発者の論文に「[Using a Command Language as a High-Level Programming Language](http://grosskurth.ca/bib/1976/mashey-command.pdf)」というのがあります。これが理由の一つですが、タイトル通り PWB シェルはプログラミング言語として使うことを目的として開発されています。（High-Level Programming Language = 高水準言語は言い過ぎかもしれませんが当時の状況を踏まえてあまり突っ込まないようにしましょう。）Command Language（コマンド言語）というのは聞き慣れない用語かもしれませんがシェルスクリプトの言語名相当の呼び名で、現在の POSIX シェルの言語仕様を書いたページのタイトルも [Shell Command Language](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html) （シェルコマンド言語）です。

残念ながら PWB シェルは Thompson シェルとの互換性を保つ必要から最小限の機能拡張にとどまり、手続き型プログラミング言語と言えなくはないですが機能的には Windows のバッチファイル程度にしかなりませんでした。しかし重要な点は当時のシェルスクリプトに「プログラミング言語として使う」ことが求められていたということです。もちろんその次のシェルである Bourne シェルにもプログラミング言語としての能力が求められました。

一般的にソフトウェアというのは互換性を保ってバージョンアップすることを選びます。しかし Bourne シェルでは互換性を切り捨てて新しく再設計されています。それは Thompson シェルの設計では求められていたプログラミング言語としての能力を組み込むのが不可能であることが PWB シェルによって明らかになっていたからです。そのため Bourne シェルは互換性を切り捨てて新たに設計・開発を行いました。こうやって生まれたシェルスクリプトがプログラミング言語にならないわけがありません。他にもシェル開発者のバックグランドや設計思想を知ることでプログラミング言語として開発されたことがはっきりと分かるのですが、それらはシェルの歴史を追いながら確認していきたいと思います。

## 年表

Unix、プログラミング言語、シェル関連の歴史をまとめてみました。Bourne シェルは Unix とともに出荷されておりシェル自体にバージョン番号がないため SVR2 (System V Release 2) のように Unix のバージョンを併記しています。実際には Bourne シェル は Unix ベンダーによって修正が加えられて出荷されているために機能が異なる[細かい亜種](https://www.in-ulm.de/~mascheck/bourne/)が多数あります。また AT&T の手によるもの、または関わっているものには後ろに@をつけています（at マークだけに）。こうしてみると AT&T の功績は偉大ですね。ただし AT&T 所属の開発者の功績は認めていても AT&T という会社としてはあまり良く思われていなかったようです。（「Unix 考古学」参照）

https://github.com/dspinellis/unix-history-repo

- Unix 登場以前
  - 1954年 FORTRAN、1958年 ALGOL, LISP、1959年 COBOL、1964年 PL/I, BASIC
  - 1960年 **ALGOL60**（構造化プログラミング言語。ほぼすべての言語の祖先）
  - 1967年 Simula （初のオブジェクト指向言語）
  - 1968年 ダイクストラ「Go To Statement Considered Harmful」
     - GOTO文有害説（本人が希望したタイトル「GOTO 文を使うべきでないケース」）
  - 1969年 Multics@（マルチタスク OS）
- 1969年 **Unix 誕生**@ （当時はシングルタスク OS、ソースコードは公開されていた）
  - アセンブラで記述、PDP-7（メモリ 16KB）で動作
- 1971年 Thompson シェル@ （パイプ、リダイレクトあり）、Unix Version 1@
- 1972年 **C 言語誕生**@、Smalltalk、Unix Version 2@
- 1973年 `ed`@, `tr`@, `grep`@、Unix Version 3@、Unix Version 4@ (C 言語）
- 1974年 `sed`@、Unix Version 5@（マルチタスク OS になる）
  - クヌース「Structured Programming with Goto Statements」
     - GOTO 文による構造化プログラミング（GOTO文有害説の終焉）
- 1975年 PWB シェル@、Unix Version 6@
- 1976年 `vi`
- 1977年 PWB/UNIX@
- 1978年 C シェル (csh)
- 1979年 Bourne シェル [Version 7]@（[ソースコード](https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh) ALGOL68 風 C 言語）、`awk`@
- 1981年 Bourne シェル [System III]@、tcsh
  - **Unix がクローズドソースになる**
- 1983年 Bourne シェル [SVR1]@、Korn Shell (ksh)@（おそらく発表のみ）、C++
  - **GNU プロジェクト発表**
- 1984年 Bourne シェル [SVR2]@（ALGOL68 風からの脱却、関数定義が可能になる）
- 1986年 Bourne シェル [SVR3]@（関数で再帰呼び出しできるようになる）、ksh86@ （ソースコード公開？）
- 1987年 Perl、pdksh
- 1988年 ksh88@（クローズドソース）
  - **POSIX 標準化 (POSIX.1  Core Services) (IEEE Std 1003.1-1988)**
- 1989年 Bourne シェル [SVR4.0]@（ジョブコントロール追加）、GNU bash、ash
  - NeXTSTEP 登場 (Darwin の前身）
- 1990年 zsh
- 1991年 **Linux 誕生**、Python
- 1992年 Bourne シェル [SVR4.2]@（[ソースコード](http://heirloom.sourceforge.net/sh.html) Heirloom プロジェクト版）
  - **POSIX シェル標準化 (POSIX.2 Shell and Utilities) (IEEE Std 1003.2-1992)**
  - GNU コンポーネント完成（GNU Hurd カーネル以外）、初の Linux ディストリ登場
- 1993年 ksh93@ (クローズドソース、2005年の 93q から[オープンソース](https://github.com/att/ast)）

参考

- https://unix.stackexchange.com/questions/145522/what-does-it-mean-to-be-sh-compatible
- https://en.wikipedia.org/wiki/History_of_Unix

## Thompson シェル 

Unix の最初のバージョンに搭載されたシェル（コマンドインタプリタ）でケン・トンプソンによって開発されました。Bourne シェルとの互換性はありません。スクリプトは単に外部コマンドを羅列しただけのものでシェル自体に制御フロー命令もないため、プログラミング言語としてはかなり力不足です。主な特徴は以下のとおりです。

マニュアル：https://www.in-ulm.de/~mascheck/bourne/v6/

- スクリプトは標準入力から入力する
- シェルスクリプトでフィルタを書けない
- 制御フローや変数がない
- `if` が外部コマンド
  - 例 `if -r /path/to/file echo /path/to/file is readable`
- `goto` も外部コマンド
- glob も外部コマンド `/etc/glob`
- パイプやリダイレクトはこの頃からある

「シェルスクリプトでフィルタを書けない」というのが少しピンときませんでしたが、要するに `sh < script` 言う形でシェルがスクリプトの入力のために標準入力を使ってしまうのでが他の入力を受け入れる方法がないからのようです。

またシェル自体に制御フロー命令を持っておらず if や goto を行いたい場合は外部コマンドの `if` コマンドや `goto` コマンドを呼び出して実現するとのことです。if はともかく goto というのは次に実行する行を指定したラベルの位置までジャンプしないといけないわけで、最初にこれを知った時 「え！！外部コマンドで goto を！？」と思ったのですが、スクリプトを標準入力から入力するという事と「`goto` コマンドでファイルポインタを移動する ([called an external goto to move the filepointer](https://www.in-ulm.de/~mascheck/bourne/))」というのを見て「出来らあっ！」と気づきました。

つまり実行するスクリプトを標準入力で渡しているわけですから、その標準入力の内容を `goto` コマンド側で消費させてやればよいのです。シェルスクリプトで同様の処理を実装してみました。

例えば次のようなスクリプトファイルがあった時

```sh:script
echo "cmd 1"
echo "cmd 2"
goto LABEL2

: LABEL1
echo "cmd 3"

: LABEL2
echo "cmd 4"
```

次のようなシェルスクリプト（Thompson シェル 相当）と `goto` コマンド相当を書くだけで同じ動きを再現することが出来ました。

```sh:thompson.sh
#!/bin/sh
PATH="$PATH:."
while IFS= read -r line; do
  eval "$line"
done
```

```sh:goto
#!/bin/sh
while read -r cmd label; do
  [ "$cmd $label" = ": $1" ] && break
done
```

```console
$ ./thompson.sh < script
cmd 1
cmd 2
cmd 4
```

うまいこと考えてますね。~~ただしこのやり方ではスクリプトの処理は前から後への一方通行しか行なえません。つまり `goto` で前の行に戻ったりループを実装することが出来ないということです。最初からそのような使い方を想定していなかったということがはっきりわかります。~~ **2021-07-24 パイプから入力した場合と勘違いしていたため訂正** （パイプからの入力ではなく）ファイルをリダイレクトを使って標準入力に与えた場合はファイルはシーク可能です。つまりこの `script` はシーク可能であるため実行行を巻き戻すことが可能です。POSIX シェルではファイルのシーク機能がないので、それができる ksh スクリプトで巻き戻し可能な `goto` を実装してみました。

```ksh:goto
#!/usr/bin/env ksh
<#((0) # ファイルの先頭にシーク
while read -r cmd label; do
  [ "$cmd $label" = ": $1" ] && break
done
```

```sh:script
: loop
date
sleep 1
goto loop
```

ちなみに `:` で始まるラベルを「ラベル」として解釈するのは `goto` コマンド側であることに注意して下さい。Thompson シェルから見ると `:` はラベルではなく単に何もしないコマンドでコメントを書くときにも使われていたようです（当時は `#` がない）。これは現在のシェルにもある `:` コマンドのルーツでしょう。

ところで Thompson シェル と `goto` コマンドはどちらが先に生まれたのでしょうか？ Thompson シェルを作った後に goto が実現できることに気づいてコマンドを作ったのか、それとも goto を実現するために Thompson シェルのスクリプトを標準入力から入力するようにしたのか、ページの順番はよくわかりません。

**2021-07-24 追記** ↑はおそらく同時に考え出されたものだと思います。[2021-07-08 に公開された oil シェルのブログ記事](https://www.oilshell.org/blog/2021/07/blog-backlog-1.html)経由で [Thompson シェルの最初の論文](https://github.com/susam/tucl)があることを知りました。それによるとシェルをコマンドとして使うことで、スクリプトを実行できるとあり `goto` を使った例が紹介されています。（この例により `goto` が巻き戻し可能であることに気づいたため、記事を訂正・加筆しました。）

## PWB シェル (Mashey シェル)

PWB は Programmer's Workbench の略でプログラマのための開発ツール群・・・でいいのかな？ちゃんとわかってない気がしますが、AT&T がプログラマー向けにカスタマイズした Unix である [PWB/UNIX](https://en.wikipedia.org/wiki/PWB/UNIX) というのがあって、PWB シェル (Mashey シェル) はその OS で採用されたシェルのようです。これも Bourne シェルとの互換性はありません。開発者の名前はジョン・マシェイです。

「[Using a Command Language as a High-Level Programming Language](http://grosskurth.ca/bib/1976/mashey-command.pdf)」という論文のタイトルからも明らかなようにシェルをプログラミング言語として使えるよう開発したものですが Thompson シェルと互換性をもたせることも目的としていたため大幅な改良はできませんでした。しかし後のシェルに影響を与えるような仕様やアイデアも生まれました。制御フロー命令がシェルに組み込まれたため一応は**手続き型プログラミング言語**と言えると思いますが、変数名が 1 文字など制限も大きため、どちらかと言えばプログラミング言語にするのを試みた言語という扱いが良さそうです。

PWB シェルの特徴は以下のとおりです。

マニュアル：https://www.in-ulm.de/~mascheck/bourne/PWB/

  - スクリプトを標準入力からだけではなくファイルとして読み込めるようになった（`sh <FILE> [ARGS]`）
  - フィルタとして使えるようになった
  - 変数が導入された（ただし変数名に 1 文字しか使えない上に一部は特別な意味に割り当てられた）
  - `HOME` や `PATH` に相当する特殊変数というアイデアが生まれた（この時点の UNIX に環境変数はありません）
  - 変数を参照するための `$` （とダブルクォート内での展開）が生まれた
    - （多くの言語で使われる `$` の起源はここにあるようです）
  - プロセス番号を取得するための `$$` が追加
    - （当時の最も多いユースケースは一時ファイルをユニークな名前にするためらしい）
  - `if-then-else-endif` や `switch-ラベル-endsw` が登場した
  - シェルスクリプトへの引数（`$1`, `$2`, ...）が登場し `shift` が生まれた
  - `goto` がシェルに組み込まれた
  - `expr $a + 1 | = a` という書き方で標準出力を変数（`a`）に代入できる
  - `#` によるコメントはまだない
  - パラメータ置換 ${-=?+} が使えるようになった


余談ですが、文法を見ていて Windows (MS-DOS) のバッチファイルが似ていると思いました。バッチファイル（を実行する `command.com`）は MS-DOS の前身とも言われている QDOS の頃、つまり 1980 年頃に作られたようです。MS-DOS は CP/M を参考にして作られたとも言われてるので CP/M に起源があるのかと思いましたが CP/M のシェルに相当する CCP (Console Command Processor) にはバッチファイルのような一連のコマンドをファイルにして実行する機能を探しきれませんでした。PWB シェルが 1975年、Bourne シェルが 1979年ということを考えると Bourne シェル を参考にするには間に合わず PWB シェル を参考にしたのかもしれません。

## C シェル

Bourne シェルと同時期に作られたシェルです。カリフォルニア大学バークレー校の大学院生だったビル・ジョイが開発しました。構文は C 言語に近く（というほど似てません）インタラクティブシェルとしての使い勝手は Bourne シェルよりも優れていました。しかしシェルスクリプトとしては書きづらいと言われています。それでも一定の支持を得ており改良版の [tcsh](https://github.com/tcsh-org/tcsh) は現在もメンテナンスが行われています。

C シェルの文法の基本だけを見ればそんなに悪いところはなさそうに見えるのですが、実際は基本的な部分でいろいろ問題があり、私も自作のツールを tcsh にも対応させようと少しだけ使ったのですが関数なかったり変数に改行だかクォートだかを入れることが難しく（できない？）バグに近い仕様が多くて断念したことがあります。といっても批判できるほど詳しくないので有名なドキュメント「[有害な csh プログラミング](http://www.speech-lab.org/~hiroki/csh-whynot.euc)」と「[wikipedia の C シェルの批判](https://ja.wikipedia.org/wiki/C_Shell#%E6%89%B9%E5%88%A4)」を紹介して終わります。

## Bourne シェル

さてこの記事の本命、スティーブン・ボーンが開発した Bourne シェルの話です。結論を言うと Bourne シェルの言語は**手続き型（構造化）プログラミング言語**です。Bourne シェルは Thompson シェルと PWB シェルを意識しつつも互換性を保つことは目標とせず一から設計されました。

ALGOL という言語をご存知でしょうか？初めて構造化プログラミングを実現した言語で C 言語を含むほとんどのプログラミング言語は ALGOL の影響を受けていると言われています。Bourne シェルのルーツに Thompson シェル と PWB シェルがあるのは当然のこととして [ALGOL68](https://en.wikipedia.org/wiki/ALGOL#ALGOL_68) もルーツと言っていいのではないかと思います。なぜそう言えるのかと言うと開発者のスティーブン・ボーンは ALGOL 68C（ALGOL 68用のコンパイラ）の開発に関わっていた人物で Bourne シェルの文法にもその影響が現れているからです。

例えば `if` の終わりの `fi`、`case` の終わりの `esac` というスペルを逆にした単語は ALGOL が発祥です（`do` の終わりが `od` でないのは [od コマンドがあったから](https://unix.stackexchange.com/questions/256149/what-does-esac-mean-at-the-end-of-a-bash-case-statement-is-it-required)と~~する説が有力っぽいです~~ [POSIX のドキュメント](https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_09_13)に書いてありました。`because od is already the name of a standard utility.`）。また批判の対象にもなっていましたが初期の Bourne シェルのコードは C 言語でありながら [ALGOL68 風](https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh) に記述できるようにマクロが多用されていました。ALGOL は制御構造を自在にネストにできる初の言語ですが Bourne シェル も同じようにネストすることができます。

Bourne シェルは関数による抽象化（一連のコードに適切な名前をつけてわかりやすくする事）が行え、順次・反復（`while`, `for`）・分岐（`if`, `case`）がシェルに組み込まれており `goto` 文もないため、構造化プログラミングや構造化定理を意識していることに間違いないでしょう。（ちなみに C 言語には関数内にしかジャンプできない制限によって乱用が抑えられているものの `goto` 文があります。）

今となっては基本の言語仕様に思えるでしょうが当時は `goto` 文有害説の真っ只中です。過去のシェルとの互換性を切り捨て、ALGOL68 と深い関係があるスティーブン・ボーンが設計し、構造化プログラミング言語や構造化定理が取り入れられている Bourne シェルの言語がプログラミング言語でないわけがありません。

### The A-Z of Programming Languages: Bourne shell, or sh

スティーブン・ボーンがどのような考えや設計方針で Bourne シェルを開発したかは [The A-Z of Programming Languages: Bourne shell, or sh](https://www.arnnet.com.au/article/279011/a-z_programming_languages_bourne_shell_sh/)  （もともとは 2009 年の [computerworld.com.au のインタビュー](https://web.archive.org/web/20100111203059/http://www.computerworld.com.au/article/279011/-z_programming_languages_bourne_shell_sh/)）で詳しく語られており、この話からも Bourne シェルをプログラミング言語として設計していたことがわかります。**このインタビュー記事は絶対に読むべきだと思います** （なぜ私はこれを今まで知らなかったのか？）最初はこの記事に詳細にコメントしようかと思っていたのですが、長くなりすぎたのでシェルスクリプトに関するポイントだけ紹介します。とくに最後の未来の有望なプログラマーに対してのアドバイスはとてもためになるのでインタビュー記事をぜひ読んで下さい。

- **Bourne シェルを作成したきっかけはオリジナルのシェルが真の言語ではなかったから**
- **プログラマブルなスクリプト言語 かつ インタラクティブシェルの両方で使えるように設計した**
- C 言語の関数呼び出しの `()` は入力が苦痛であるため、引数をスペース区切りとしクォートも不要な場合は省略できる文法とした
- **環境変数は以前から Unix に搭載されていた機能ではなく、シェルスクリプトのために導入した新機能**
- **`if` や `for` などの制御フロー命令を支持し `goto` は意図的に削除した**
- **関数は最後に追加されたが最初から追加してしかるべき機能だった**
  - 2021-08-15 追記 ["ksh - An Extensible High Level Language](https://www.in-ulm.de/~mascheck/bourne/korn.html) によると ksh の方がわずかに早く実装していたようです。
- （外部）コマンドと関数は抽象的なレベルでは同一のものである
- パフォーマンス問題の多くは外部コマンドの実行によるもので、関数はそれを解決するためのもの
- Bourne シェルの当初の設計により、これ以上の機能追加は複雑になると判断し追加することを止めた
- Bourne シェルのコンパイラも作成しようとしていたが、誰もパフォーマンスに不満がなかったためやらなかった
- ALGOL のような構文を採用したのは `{}` の対応が分かりづらいから
- `if ... then ... fi` や `while ... do ... done` の `...` の部分を修正なしに再利用できるように設計した（という意味？）
- PowerShell のアプローチは、Unix 環境には「オブジェクト」がないため、Unix シェルに有効なのものなるかは不明
- bash は Bourne シェルと厳密な互換性を持ったオープンソース実装だと考えている
  - （私の意見では bash は Bourne シェル よりも当時クローズドだった ksh を参考としているように思えます）
- シェルスクリプトはコードが分かりづらく遅いという意見があるが、これに賛成しますか？という質問に対し
  - シェルスクリプトの方が簡単に読みづらいコードになるかもしれないが、どんな言語でも読みづらいコードを書けることはできる
  - シェルは文字列処理言語で、文字列処理はシンプルであるため効率的に実行できないわけがない
     - （私の意見ではこれは文字列が英文のような複数のスペース区切りで単語単位で処理する前提の話で、文字単位での加工を行う場合は非効率です。後継のシェルではパラメータ展開により改善されました）
  - コマンドファイルは実行前に処理済みであり、他のインタプリタ型言語と同程度の効率
  - ただしあまり処理を行わない外部コマンドを多数実行する場合は `fork` と `exec` によって非効率的（遅い）
- シェルはこれからどうなっていくのか？という質問に対して
  - **シェルは Unix 環境へのインターフェース**
     - （Unix をコマンドが詰まった工具箱と考えればしっくり来ると思います）
  - ウェブという環境が生まれたから PHP などの言語が登場した
  - 新しい環境が登場すればそれに適した言語が登場するだろう
- 自分で Bourne シェル と名付けたのではなく、勝手に誰かが言いだした
- （プログラミング言語一般の話として）言語自体と同じぐらいライブラリセットが重要
- **自分のコードに責任を持ち、徹底的にテストし、実際の動作環境を良く知り、ユーザーが何をしたいのかを理解することが重要**

### その他の情報

- [BSDCan 2015 The Technical BSD Conference](https://www.bsdcan.org/2015/schedule/events/612.en.html)
  - sh as a language: Typeless / Strings are first class and only citizens
  - Shell Design and implementation:  ALGOL 68 concepts
  - C 言語 vs シェルスクリプト: C 言語の `if` だと先読みが必要になってインタラクティブには適さない（？）
  - 他 シェルスクリプトの文法をそうした理由 （とかあまりじっくり見てません）

### なんの語録？

余談ですが[日本語の Bourne シェルの wikipedia](https://ja.wikipedia.org/wiki/Bourne_Shell)に次のような「語録」が書かれていました。

> 誰も Bourne shell の文法がどうなっているかを本当には知らない。ソースコードを調べてみてもほとんど役に立たない。— Tom Duff

なんの説明もなくいきなり登場しているので、どこからこれが出てきたのか気になって英語ページを調べた所、現在は削除されていましたが履歴を見ると Linux JOURNAL の [What's GNU: Bash&mdash;The GNU Shell](https://www.linuxjournal.com/article/2800) (1994 年）の記事が元ネタのようです。この記事で引用されていた Tom Duff の言葉だけを抜き出しているようです。この記事を書いた Chet Ramey は bash 開発者の一人で Tom Duff は Plan 9 の rc シェルの開発者です。記事を読む限りこの言葉は Bourne シェル の論文の文法や細かい実装（のバグ？）と完全な互換性を bash で実現するのが難しいという話の中で引用された言葉のようです。POSIX シェルに関しては POSIX.2 で文法が定義されているため、この話は当てはまらないでしょう。現在のシェルスクリプトの文法が悪いと言ってるのと勘違いするところでした。

## KornShell

Bourne シェル は Unix のシステムシェルとして広く使われることになりましたが、AT&T ではさらによいシェルが求められており Bourne シェル の完全な上位互換のシェルである KornShell がデビッド・コーンによって開発されました。Bourne シェルは C シェルが持っていたようなインタラクティブシェルとしての機能（ジョブコントロール、コマンド履歴等）が弱かったのですが ksh にはそれらの機能が取り入れられています。

wikipedia には 最初のリリースが 1983 年と書かれてありますがおそらくこれは発表のみで、オライリーの「[Learning the Korn Shell](https://www.oreilly.com/library/view/learning-the-korn/0596001959/ch01.html)」によると 1986 年に Experimental Toolchest として一般公開されたようです。ライセンスはよくわかりませんが、このバージョンはソースコードも安価で提供されたと書かれています。代表的なバージョンは ksh86、ksh88、ksh93 です。（[細かいバージョン](https://www.in-ulm.de/~mascheck/various/shells/ksh_versions.html)はこちら。ksh86 のソースコードへのリンクもあります。）

ksh86 はおそらく限定的にしか公開されておらず、私も使ったことがないので詳細は割愛します。最初に広く普及したのは 1988 年にリリースされた ksh88 です。最初にリリースされてから何回か機能追加を伴うバージョンアップが行われており最終バージョンは 88i です（末尾のアルファベットが増えていくスタイル）。POSIX では ksh88 をベースに POSIX シェルを策定しました（POSIX.2 1992年）。ただし ksh88 のすべての機能が POSIX シェルとして採用されたのではなく、例えば配列は採用されませんでした。以下は（POSIX には含まれない）ksh88 の機能の一部です。

- 配列
- `[[ ]]` （この時点では正規表現対応はない）

POSIX シェルが策定された次の年である 1993 年に ksh93 がリリースされました。ksh 93 では更に機能追加されており POSIX シェルを超えるさまざまな機能が追加されています。その後もいくつかの機能追加を伴うリリースが行われており AT&T がリリースした公式の最後のバージョンは 2012年にリリースされた 93u+ です。ksh88 / ksh93 はもともとはクローズドソースでしたが 2005 年の 93q よりオープンソース化され多くの環境に移植されています。以下は追加されている機能の一例です。（[英語版の ksh の wikipedia]( https://en.wikipedia.org/wiki/KornShell) にも機能の一覧がまとめられています。）

- 連想配列
- 浮動小数点演算
- 数学関数
- 正規表現サポート
- 複合変数（`${point.x}` のような参照ができる変数）
- `/dev/tcp/host/port` や `/dev/udp/host/port` によるネットワークアクセス
- オブジェクト指向プログラミング（この記事を書いてる時に知りました。継承もできるだと！？）

これらの機能からも分かる通り ksh93 ではさらにプログラミング言語としての機能が強化されています。

余談ですが日本語版の wikipedia は（現時点で）かなり更新されておらず、例えば「ksh93は作者であるコーンがいまだに保守している」と書いてありますが、英語版にはデビッド・コーンは 2012 年に AT&T を去っており 2014 年まで ksh93v-（ベータ）ブランチで作業を続けたと書いてあるため（おそらく）今はメンテナンスをしていません。その後 ksh93v- はコミュニティによって引き継がれ ksh2020 がリリースされましたが互換性の問題が発生し AT&T はコミュニティの修正をロールバックして最後の安定版である 93u+ から再始動することを決定しました。現在は（AT&T ではない外部の開発者によって） [ksh 93u+m](https://github.com/ksh93/ksh) でバグの修正をメインとした開発が行われています。

### David Korn Tells All

2001 年に本家の [slaskdot に投稿された記事](https://news.slashdot.org/story/01/02/06/2030205/david-korn-tells-all)です。こちらもいろいろと興味深いことが書かれています。いくつか気になったポイントを紹介します。

- **ksh93 のスクリプト機能は他のどのシェルよりも高度であり perl/tcl/python のカテゴリに属していると思っている**
- **ksh93 が主にフォーカスしてるのはスクリプティングであり、この点では bash を凌駕している**
- ksh93 はビルトインの追加やライブラリとして使うための C言語 API を備えたライブラリとして実装されており拡張性が高い
- pdksh は ksh88 のクローンで MKS の ksh よりも優れている
  - （下記の逸話につながってると思われます）
- 将来のリリースではバイナリデータを処理する機能と名前空間を追加したいと思っている。
  - （ファイルのシークや複合変数などがあるため部分的には実現されてる気もしますが完全な形ではないと思います）
- **Unix は単なる OS ではなく物事を行うための手段。シェルはそれを機能せるための接着剤（グルー）**
- AST Toolkit は（手段としての Unix を強化するために）改良されたツールを提供するのが目的
  - （ようやく [ast](https://github.com/att/ast) リポジトリの中に ksh が含まれてる理由がわかりました）
- GUI インターフェースは Unix の方法論に敵対的である
- GUI はシステムから分離し GUI でできることはスクリプトを使って GUI なしでできるようにしておくべき
  - （私もウェブサービスや機能をブラウザや GUI からだけでなく CLI やテストから実行できるように設計しています）
- ksh で満足してない機能・コードはなんですか？という質問に対して
  - もっとも後悔してるのはコードそのものではなく Bourne シェルから受け継いだシェルの言語仕様のミス
  - バッククォートをなくして `$()` にする
  - ダブルクォートで括られた文字は ANSI-C のバックスラッシュ規則を全て解釈させる
  - `$` の拡張も行う。（何をしたいのかよくわかりませんでした）
  - `echo` コマンドは引数の解釈を行わないようにする
  - 単語分割をデフォルトで無効にする（zsh ではデフォルトで無効です）
  - ただし、これらの変更をしなかったため zsh とは異なり安全に `/bin/sh` を置き換えることができる
  - fc コマンドはデザインが悪かった（私は使ったことがないのでよくわかりません）
  - `let` コマンドは `((..))` に取って代わられた（`let` はあまり使わないほうが良さそうですね）
  - シェル変数の属性のエクスポートはするべきではなかった（下記の補足参照） 
- ウェブのための ksh プログラミングについて
  - **ヒアドキュメントは CGI スクリプトに適しているが、ksh93 の機能は ksh88 よりもはるかにウェブに適している**
  - ksh88 には、CGI に渡される無数の置換を行うための演算子がない
  - ksh93 の複合変数は、これを効率的に行うことができる。
  - kornshell.com のページにはCGIのすべての引数をシェル変数にマップするスクリプトコードがある（どれだろう？）
  - ksh93 には HTML/XML の%xx 形式で文字列展開を行える %H が printf に組み込まれている
  - ksh のウェブサイトは ksh93 スクリプトと AT&T nmake で管理されている
- ksh88 は POSIX に完全に準拠しておらず一部のベンダーは ksh88 を修正することで対応している
   - （あ、そうなのか、ksh88 は POSIX に完全に準拠してないという扱いなのか・・・）
- ksh93 は 殆どの部分で Perl5 の機能を持っておりパフォーマンスも同等。
   - （作者の考えであり、考え方には個人差があります）
- ファイルシステムやパイプラインを多用する場合はシェルスクリプトが適している傾向がある

補足 シェル変数の属性のエクスポートについて：ksh では変数に整数型などの属性をつけることができ `export` したときに特殊な環境変数 `A__z` を介して子 ksh プロセスに属性が継承されます。私の意見としては Unix の環境変数には属性という概念がないので（デビッド・コーンも言ってるように）するべきではなかったと思います。現在メンテナンス中の 93u+m では新たに POSIX モード（`set -o posix`）が追加され POSIX モードでは属性のエクスポートが無効になります。また POSIX モードに関係なくセキュリティ上の懸念から `readonly` 属性は子 ksh プロセスにエクスポートできなくなります。（親プロセスから任意の読み取り専用＝初期化不可能な値の変数を与えることが可能になるため）

#### 逸話

[1998 年 USENIX NT conference でのミラクル](https://web.archive.org/web/20150310123118/http://gopher.quux.org:70/Humor%20and%20Fun/Microsoft_KSH.txt%7C/MBOX-MESSAGE/1)

マイクロソフト「（MKS 版）KornShell を組み込みこんだ [UNIX 統合パッケージ](https://ja.wikipedia.org/wiki/Microsoft_Windows_Services_for_UNIX)をリリースします」
50 代の男「他のもっと互換性があるシェルを検討したほうが良かったのでは？」
マイクロソフト「これは高い互換性があって既存のシェルスクリプトがそのまま動く！」
50 代の男「それは本物の KornShell ではなく ksh88 との互換性もなく・・・」
マイクロソフト「その話は間違ってる！ 本物の KornShell を選んだ！」
50 代の男「・・・」

別の人「その男はデビッド・コーンです」
マイクロソフト「・・・」

注意 ↑の話は記事やメールの内容を参考に作り変えてます。本当の会話の内容は記録がないでしょう。

### その他の情報

- [KSH-93 Frequently Asked Questions](http://www.kornshell.com/doc/faq.html)

## Bash (Bourne-again shell)

bash は ksh88 登場の次の年である 1989 年に GNU プロジェクトの成果の一つとしてブライアン・フォックスによって開発されました。1983 年に発表された GNU プロジェクトの成果の一つで Linux でデフォルトで採用されていたこともあって利用者が多いシェルです。インタラクティブシェルとして使われることが多いですが、一部のシステムではシステムシェル（`/bin/sh`）としても使われています。

私も Linux ではデフォルトのインタラクティブシェルとして使っているのですがシェルスクリプトを実行するシェルとしては一部の拡張機能を使う程度で bash に依存したコードは基本的に書いておらず実はあまり詳しくありません。当時クローズだった ksh のオープンソース実装という認識で bash の機能の多くは ksh が由来であると考えています。情報も多く調べればいろいろわかると思うので詳細について割愛します。

## pdksh （→ mksh）

ksh がクローズドだったときに ksh のパブリックドメイン版として開発されたシェルです。一人の開発者の手によるものではなく複数の開発者に引き継がれているようです。（[loksh の CONTRIBUTORS](https://github.com/dimkr/loksh/blob/master/CONTRIBUTORS) より）

- ? - 3.2 (1987 - 1989?) - Eric Gisin （Charles Forsyth の public domain V7 shell がベース）
- 3.3 ? (date?) - John R MacMillan
- 4.0 - 4.9 (1991/11 - 1994/7) - Simon J. Gerraty
- 5.0 - 5.2 (1994/7 - 1999/7 ?) - Michael Rendell

pdksh は ksh88 相当（ksh93 ではない）の機能を持っており本家の開発は 1999 年頃で終わっていますが、POSIX シェル策定以降の 1997 年に OpenBSD で システムシェル（`/bin/sh`）として採用されています。また MirOS BSD 用として（2002年頃？に）フォークした mksh では ksh88・POSIX シェルを超える機能が追加され今もメンテナンスされており、Android 4.0 以降のシェルに採用されています。

余談ですが、私は Android のシステムに詳しくないので Termux で確認してみたのですがシェル（mksh）のパスは `/system/bin/sh` で `/bin/` ディレクトリがありませんでした。しかしシバンが `/bin/sh` のスクリプトが問題なく動いたため調べてみた所どうやら `LD_PRELOAD` で指定されている `libtermux-exec.so` によって互換性が実現されているようです。結局の所 Termux を使っても本物の Android 環境とは呼べないようでどうやって本物の Android 環境でシェルスクリプトの動作テストすればいいのか悩んでいます。（root 化は面倒だしそれが本物と言えるのかも疑問だし、そもそも Android の開発環境というのをよく知らない）

## ash （→ dash, BusyBox ash）

1989 年にケネス・アルムクィストによって作成されたシェルです。当初は Bourne シェル のクローンとして作成されました。軽量かつ高速なシェルでシステムシェル（`/bin/sh`）として最適とされています。

POSIX シェル策定以降の話になりますが 1993年に NetBSD が 1996年に FreeBSD がシステムシェルとして採用しました。1997年頃に Herbert Xu によって NetBSD sh から Debian 向けに移植されており 2002年に dash (Debian Almquist shell) に名前が変わりました。その後 2006年の Ubuntu 6.10 で、2011 年の Debian 6.0 でシステムシェルとして dash が採用されました。これらの亜種は一部の例外（`local` コマンド）を除き POSIX で規定されていない拡張機能を持たないため POSIX シェルに限りなく近いシェルです。

2001 年頃に組み込み向けの BusyBox に dash が ash として組み込まれました。Busybox ash は dash （やその他のフォーク）と異なり POSIX 準拠よりも bash とある程度の互換性を実現を目指しているようで一部の拡張機能（`[[ ]]` や `$EPOCHREALTIME` 等）が組み込まれ始めています。軽量であるはずの BusyBox の ash の方がもっとも高機能な ash / dash という逆転現象が起きてるのは面白いです。

## zsh (Z Shell)

1990 年には zsh が登場します。某 Z 戦士の活躍が 1989 年から始まったので名前の由来に何かしらの関係があるだろうと調べたのですがわかりませんでした。一説では Zhong Shao という教授の名前が由来らしいです。最初は csh のサブセットになることを想定していたらしいです。それが想定以上に拡張され 1.0 時点では ksh のような優れた設計と tcsh のようなインタラクティブシェルとしての機能を兼ね備え、多くの機能を搭載するという目標になったようです。（この理屈であれば csh の文法と互換性があることになるはずですが、私が csh に詳しくないのでどれだけ互換性があるのかは知りません。）

zsh は システムシェル（`/bin/sh`）として使われたことはなかったと思っていたのですが、いろいろと調べていた所どうやら Mac OSX 10.0 - 10.1 で  zsh (3.0.8 - ?) が使われていたようです（[参考](https://www.in-ulm.de/~mascheck/various/shells/) 現在はご存知の通り bash が使われています）。それ以外ではシステムシェルとしては使われたことはないと思いますが、デフォルトのインタラクティブシェルとしては macOS や Kali Linux で採用されています。

当初の目的が csh であることから Bourne シェルとの互換性はそれほど重要視していなかったと思われます。単語分割をデフォルトで行わないなど Bourne シェル（または POSIX シェル）と動きが異なる部分がありデフォルトの状態では POSIX シェルと互換性が低いという印象を持っています。もちろん設定で POSIX 準拠に近づけることは出来ます。単に開発者が良いと考えている設定をデフォルトにしたというだけでポリシーが違うだけのことです。私は macOS では zsh をインタラクティブシェルとして使用しているますが、カスタマイズもあまり行っておらず詳しくないので詳細は割愛します。

## POSIX シェル

現在使用されているシェル（もしくはその祖先）の多くが出揃った後の 1992 年に POSIX シェルが POSIX.2: Shell and Utilities で標準化されました。現在では殆どのシステムで POSIX シェルが `/bin/sh` として、またはデフォルトのインタラクティブシェルとして使用されています。

POSIX シェルは ksh88 をベースに策定されています。ただし ksh88 の仕様が全てが含まれているわけではありません。特に大きな機能で POSIX に含まれなかったのは配列です。理由ははっきりとはわかりませんでしたが構文が使いづらかかった（`arr=(1 2 3)` ではなく `set -A arr 1 2 3` と書く必要がある）という意見があるようです。また 当時の bash 1.x に配列がなかったのも理由の一つかもしれません（bash に配列が搭載されたのは 1996 年の 2.0 から）。そのため歴史の上では ksh88 から POSIX シェルが生まれているのですが、機能的には POSIX シェルに拡張機能を加えたのが ksh88 であるかのように見えます。

Bourne シェルにはなく POSIX シェルで新しく追加された機能は次のとおりです。

- 算術式展開（例 `ret=$((var + 1))`）
- パラメーター展開のうち前方削除と後方削除（`${parameter#word}` や `${parameter%word}` 等）
- <code>\`cmd\`</code> を改良した `$(cmd)`

私個人の話になりますが、このうち前 2 つは私にとって特に重要な機能でした。なぜなら算術式展開は外部コマンドの `expr` の代わりに使うことができ、パラメーター展開も外部コマンドの `sed` や `tr` を置き換えることができる機能だからです。スティーブン・ボーンも言っているようにパフォーマンスの問題の多くは外部コマンド（`fork` と `exec`）です。私がやっているシェルスクリプトによるプログラミングでは小さいデータを多く扱うため外部コマンドの呼び出しを減らすことは大きなパフォーマンス改善に繋がりました。そのため私は対応するシェルの最低ラインを Bourne シェルではなく POSIX シェルにしています。もちろん Bourne シェルはすでに古くほぼ使われてないだろうというのも理由の一つです。

### POSIX シェルへの移行状況

POSIX シェルが策定されたからと言っても各ベンダーはすぐに POSIX シェルに移行するわけではありません。互換性維持のため特に Unix では長い間  Bourne シェル がシステムシェルとして使われていたようです。たとえば Solaris 10 は 2005年にリリースされましたが `/bin/sh` は Bourne シェルです（ただし POSIX シェルである ksh88 も含まれており適切に設定すれば POSIX 準拠のシステムとして使えるのだと思います）。Solaris 10 のサポートは Oracle Premier Support が 2018 年で終了していますが Extended Support が2024年までなので、一応はまだ使われていることになります。しかしながら、[ここ](https://www.in-ulm.de/~mascheck/various/shells/) を見る限り Bourne シェルがシステムシェルとして設定された OS が出荷されたのは遅くとも 200x 年ぐらいまででリリース後 10 年以上経過しているため、ほとんどの環境で POSIX シェルへの移行は完了していると思われます。

ちなみに「[入門UNIXシェルプログラミング](https://www.amazon.co.jp/dp/4797321946)」では POSIX シェルは汎用性が悪いと書かれているようですが、この本の発行日が日本語版だと 2003 年（翻訳元はいつだろう？）なので、さすがに古い情報だと思います。

### 現在のシステムシェルはどのシェル？

ところで Unix の時代からシェルのパスは `/bin/sh` でした。では現在一番使われてる `/bin/sh` はどのシェルだと思いますか？みんなシェルスクリプトを bash で書いているのだから bash に決まっていると思いますか？（注意 インタラクティブシェルの話ではありません）

- Debian / Ubuntu ・・・ dash
- RedHat / CentOS ・・・ bash
- Alpine / OpenWrt ・・・ busybox ash
- macOS ・・・ bash  3.2.57
- Android ・・・ mksh
- FreeBSD ・・・FreeBSD sh（ash 系）
- OpenBSD ・・・ OpenBSD ksh（pdksh 系）
- Solaris 11 ・・・ ksh93

みごとにバラバラです。インタラクティブシェルとして使われているシェルは bash が一番多いと思うのですが、Linux の半数以上のシェア（WSL を入れればもっと増えそうです）を占めているが Ubuntu と Debian であることを踏まえると現在 `/bin/sh` としてもっとも使われているシェルは dash ということになるでしょう。

dash というのは POSIX シェルにとても近いシェルで配列などの拡張機能を持っていません。私の意見ではシェル = bash の流れがあったのは Ubuntu 6.10 で dash が `/bin/sh` として採用された 2006 年あたりまでです。つまり bash で書いていれば大丈夫だろうというのは古い時代の話ですでに終わっており、現在は POSIX シェルに準拠してコードを書くことが重要な時代に変わっています。本番環境が Debian 系で手元で書いたシェルスクリプトが動かなかったというような話もときどき聞きます。

各シェルの拡張機能は便利なので使っても良いと思いますが、必要ない箇所ははなるべく使わないようにするのが私のオススメです。そうすれば他のシェルへの移植性が高まるので macOS のように bash から zsh に代わるようなことがあっても修正がすく少なくてすみます。また POSIX シェルの方が機能が少ないので覚えるのも簡単です。特にシェルスクリプトを使いたくないと思ってる人が、多くの機能を持った難しい bash に挑戦して、いろんな書き方を覚えられなくて、移植性問題に苦しんで、さらにシェルスクリプトを嫌いになるという流れを見ると、だから最初から簡単な POSIX シェルにしておけばいいのにと思わずにはいられません。

### POSIX シェル 更新情報

POSIX シェルとユーティリティは 1992 年に POSIX.2 として標準化された後 POSIX.1-2001 と POSIX.1-2008 の二回改定されているようです。2004 edition とか 2018 editionというのは、規格を改定してからの細かい修正（TC - Technical Corrigenda）を反映させた訂正版ということだと思います。また POSIX と Single UNIX Specification と IEEE という別々の標準化団体があるんですが、同じ内容を共有しているそうです。（ややこしい）

仕様書の場所（すみません。ちゃんと整理できていません。POSIX より SUS の番号で区別したほうがわかりやすい？）

- POSIX.1-2008, SUSv4 （最新）
  - [The Open Group Base Specifications Issue 7, 2018 edition IEEE Std 1003.1™-2017 (Revision of IEEE Std 1003.1-2008)](https://pubs.opengroup.org/onlinepubs/9699919799/)
- POSIX.1-2001, SUSv3
  - [The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition](https://pubs.opengroup.org/onlinepubs/009695399/)
- SUSv2
  - [The Single UNIX® Specification, Version 2 Copyright © 1997 The Open Group](https://pubs.opengroup.org/onlinepubs/7990989775/)
  - the Commands and Utilities, Issue 5
- SUSv1
  - Single UNIX Specification Version 1, 1995 Edition
  - Commands and Utilities, Issue 4, Version 2
- Issue Tracker （自分用リンク。いつもなかなかたどり着けないのでｗ）
  - [Austin Group Defect Tracker](https://www.austingroupbugs.net/view_all_bug_page.php) （次の Issue 8 の drafts とかありますよ）

おまけ POSIX や IEEE の規格番号がよくわからん

- `IEEE 1003` が POSIX 関連全体 の IEEE の規格番号
- `IEEE 1003.n` の `n` がカテゴリを意味するサブ番号。その後ろにエディションを意味する西暦をつける
- 最初の仕様は `IEEE 1003.1-1988` - IEEE Standard Portable Operating System Interface for Computer Environments
- POSIX というのは元々 `IEEE 1003.1-1988` を参照するためにわかりやすくつけた別名
- POSIX だとカテゴリが曖昧になるので後に `POSIX.1` という形式のより正確な別名に変更した
- `POSIX 1003.1` という言い方は 1003 の意味が重複してるし間違い？と思うが opengroup でも使われている
- 事実上 `IEEE 1003.n-yyyy` と `POSIX [1003.]n[-yyyy]` は同じ意味と考えて良さそう
- 最初の頃はシェルとコマンドの仕様が分離されていた (POSIX.2 - Shell and Utilities)
- `POSIX 1003.2` と書いてあるものもあるが結局は `POSIX.2` のこと
- `POSIX.1-2001` で `POSIX.2` が `POSIX.1` に統合され `POSIX.2` はなくなった
- 厳密に書きたい人や 2001 年より前の資料は .1 とか .2 とかつけてるけど今は `POSIX[.1]` だけ
- その他の規格名は「[C と UNIX の標準規格](http://manpages.ubuntu.com/manpages/bionic/ja/man7/standards.7.html)」参照

参考

- http://www.opengroup.org/austin/papers/posix_faq.html
- http://www.opengroup.org/austin/papers/backgrounder.html
- https://en.wikipedia.org/wiki/POSIX
- http://stallman.org/articles/posix.html

## まとめ

この記事では POSIX シェルが登場するまでの歴史をまとめてみましたが、もう一つのテーマはシェルスクリプトがプログラミング言語であるかどうかです。現在よく使われてるシェルの多くが AT&T が開発したシェルのオープンソース版を作ることから始まっていることを考えると、シェルの進化の本流は AT&T にあったと言って良いと思います。それらを調べた結果、最初の Thompson シェルは別として PWB シェル、Bourne シェル、KornShell すべてがシェルをプログラミング言語として使えるように設計したということがはっきりと述べられていることがわかりました。

この記事をまとめるにあたって調べてた中で特に印象的だったのはスティーブン・ボーンが言った「シェルは Unix 環境へのインターフェースである」という言葉です。最近 [zx](https://github.com/google/zx) という JavaScript でシェルスクリプト相当のスクリプトを書くことが出来るツールがリリースされました。このようなツールやライブラリはいくつもありますがそれらは本当に Unix 環境へのインターフェースとして適切な言語になっているのでしょうか？インターフェースと言うからには使いやすくなければなりません。ディレクトリを移動する時、シェルスクリプトであれば `cd /tmp` と入力するだけです。しかし zx では `cd('/tmp')` と書かなければならずカッコとシングルクォートが必要になります。コマンドをオプションをつけて実行する時、コマンドとコマンドをパイプでつなげる時、zx ではシェルスクリプトよりももっと多くのコードが必要になるように見えます。例えばそれをインタラクティブシェルで入力したとして苦痛にならないでしょうか？外部コマンドを実行できるライブラリとしてはよく出来ていると思いますが、私には言語とUnix 環境の間にライブラリという名の壁があるように見えます。もちろんすべてのケースでシェルスクリプトが優れているという話ではありません。Unix という環境のためのプログラミング言語であれば、最初からそのように設計されたシェルスクリプトが適切であり、ウェブやデスクトップという別の環境では別の言語が適切という話です。

さてこれから先、シェルは一体どうなっていくのでしょうか？ほとんど今と変わらない機能を維持していくのか、それとも大幅な更新はあるのか？もし次世代の POSIX シェルが規定されるとしたら配列や追加のパラメーター展開を加えてほしいですね。でもあまり機能を加えるとシェルスクリプトで無茶をする人（ん？それ私か？）が出てくるので悩ましい所です。オブジェクト指向とか JavaScript のような新しい文法の追加とかは必要ないと思いますが、もうちょっとだけシェルスクリプトでプログラミングしやすくして欲しいです。なんてったって本物のプログラミング言語ですから！