LoginSignup
14
9

Bourne Shell(古いsh)とPOSIXシェル(今のsh, bash等)の違い

Last updated at Posted at 2021-07-29

Bourne Shell はとっくにレガシーです

Bourne シェルとは昔の UNIX で使われていた古いシェルの名前です。最終バージョン(SVR4.2 版)がリリースされたのは 1992 年なので 30 年も前のシェルです。現在は Bourne シェル の後継シェルが広く使われています。Linux、macOS、BSD 系 Unix(初期の BSD は除く)には Bourne シェルが搭載されたことがないため、古い UNIX を使ったことがある人以外は Bourne シェルを使ったことは無いはずです。Bourne シェルは近く終焉を迎えます。詳細は以下の記事を参照してください。

Bourne シェルは Steve Bourne が開発したシェルの名前です。しばしば勘違いされていますが POSIX で標準化されているシェルは Bourne シェルではありません。また Bourne シェルは POSIX シェルの標準規格の内容に準拠していません。現在使われている Bourne シェルの後継シェル(複数あります)は POSIX の仕様に(ほぼ)準拠しており、それらは総称として POSIX シェルと呼ばれています。具体的には dash、bash、ksh、mksh、yash、zsh 等のことを指しています(注意 fish は Bourne シェルとも POSIX シェルとも互換性はありません)。たまに 「sh は Bourne シェルです」と言われていたりしますが、POSIX シェルも sh (/bin/sh) として使われているため、sh という名前だけではどのシェルのことなのかわかりません。FreeBSD sh や NetBSD sh は Bourne シェルのクローンとして開発された ash をベースとしたシェルですが、FreeBSD sh や NetBSD sh は Bourne シェルでも ash でもありません。たとえ似ていたとしてもそれぞれは別のシェルで違った機能を持っています。dash、bash、ksh、FreeBSD sh、NetBSD sh など具体的なシェルの名前を言うようにしてください。

すべての POSIX シェルは Bourne シェルと(完全ではないが)互換性を持った後継シェルなので「Bourne シェル系」と呼ばれることがありますが「Bourne シェル」そのものではありません。また Bourne シェルは C シェル (csh) に対応して B シェルと呼ばれることもありますが曖昧な意味で使われており、いくつかのサイトでは Bourne シェル(系)と言いながら Bourne シェルでは動かないコードを書いていたり、紛らわしいことに Bourne シェル系の意味で B シェルという用語を使いながら Bourne シェルの略と説明しているサイトがあります。Bourne シェルという用語は正しく使われていないことが多々あるため注意してください。

POSIX でシェルが標準化されたのは 1992 年なので、それ以前は POSIX シェルと言う呼び名はありませんでした。従って、当時は一般的に Bourne シェル(系)と呼ばれており、その中に Bourne シェルと後継シェルが含まれていました。しかし現在は Bourne シェルは使わていません。話がややこしくなるので Bourne シェルという呼び方は Bourne シェルそのものを指す時だけに使い、それ以外は POSIX シェルと呼ぶことをおすすめします。

この記事の内容

Bourne シェルと POSIX シェルはよく勘違いされており私も昔混乱していたので、異なるシェルであるという根拠として具体的に何が違うのかについてまとめます。ただし詳しくまとめるつもりはありません。Bourne シェルの範囲で書けば POSIX シェルでも動くかもしれませんが、これだけ違う所があるんだから 古い Bourne シェルでも動くようにするとかいいかげんやめようぜ、Bourne シェルと呼ぶのをやめようぜ、と伝えるのが目的です。Python 2 系がサポート終了したように Bourne シェルも終了したシェルです。(実際 Bourne シェルがシステムシェルになっているサポート中の OS ってあるのでしょうか?もしどなたか知っている方がいたらコメントで教えて下さい。)

この記事で動作確認した環境と Bourne シェルは Solaris 10 の /bin/sh です。Bourne シェルには複数のバージョンがありますが、Solaris 10 の Bourne シェルのバージョンは SVR4.0 相当のようです。正確には Bourne シェルにはバージョン番号がなく、UNIX のバージョンで呼ばれていることが多いのでこの記事でもそれを使用しています。(例 SVR4 - System V Release 4)

Bourne シェルバージョン覧

  • Version 7 (1979)
  • System III (1981)
  • SVR1 (1983)
  • SVR2 (1984)
  • SVR3 (1986)
  • SVR4.0 (1989) 例 Solaris 10 の /bin/sh
  • SVR4.2 (1992)

Bourne シェルは・・・

注意 ここに書いているものは POSIX に準拠したシェルならば必ず実装しなければいけないものばかりです(各シェル固有の拡張は含めていません)。つまり Bourne シェルが POSIX に準拠してない点を述べています。

算術式展開に対応していない

$ echo "$((1 + 2))"
$((1 + 2))

$ # expr を使わなければいけない
$ expr 1 + 2
3

しばしば $((...)) は bash 拡張構文だと勘違いされますが POSIX 準拠でどのシェルでも使えます。

新しいコマンド置換に対応していない

$ echo "$(expr 1 + 2)"
syntax error: `(' unexpected

$ # 古いバッククォートのみ対応
$ echo `expr 1 + 2`
3

しばしば $(...) は bash 拡張構文だと勘違いされますが POSIX 準拠でどのシェルでも使えます。

パラメーター展開の一部に対応していない

$ var=abc
$ echo "${var#?}"
bad substitution

$ echo "${#var}"
bad substitution

$ # これには対応している
$ echo "${var:-}"
abc

$PPID に対応していない

$ echo "$PPID"

read -r に対応していない

$ read -r line
-r: is not an identifie

SVR4.2 で対応したらしい

while ... do ... done < file がサブシェルになってしまう

$ echo test > /tmp/dummy
$ var=1
$ while read line; do
>   echo "$line"
>   var=2
> done < /tmp/dummy
test
$ echo "$var" # 2 が表示されなければいけない
1

 正確にはリダイレクトを伴う複合コマンドがサブシェルになるのだと思う

export / readonly で値を指定できない

$ export A=123
A=123: is not an identifier

$ # 別々に分ける必要がある
$ A=123
$ export A

case のパターンの前に ( をつけられない

$ case 123 in (*)
syntax error: `(' unexpected

set -- で位置パラメータを空にできない

$ set -- a b c
$ set --

$ echo "$@"
a b c

$ # shift で代用できる
$ shift $#
$ echo "$@"

set -u の状態で位置パラメータがない時に $@ または $* を参照できない

$ set -u
$ shift $#

$ echo "$@"
@: parameter not set

$ echo "$*"
*: parameter not set

test ([ ]) でハイフンで始まる文字列を演算子と誤認識してしまう

$ var="-z"
$ [ "$var" ]
test: argument expected

cd -- に対応していない

$ cd -- /
--: does not exist

-- の意味は以下を参照

POSIX cd

OPTIONS
The cd utility shall conform to XBD Utility Syntax Guidelines .

POSIX 12.2 Utility Syntax Guidelines

Guideline 4:
All options should be preceded by the '-' delimiter character.

$PWD / $OLDPWD に対応していない

$ cd /tmp && cd /
$ echo "$PWD"

$ echo "$OLDPWD"

cd - (前のディレクトリに戻る)に対応していない

$ cd -
-: does not exist

~ が展開されない

$ cd ~
~: does not exist

set -C に対応していない

$ set -C
-C: bad option(s)

set -o を使った長いシェルオプション名に対応していない

$ set -o errexit
-o: bad option(s)

IFS がコマンド名を単語分割する

IFS=l
echolocation # echo "ocation" が実行される

IFSunset できない

$ unset IFS
IFS: cannot unset

POSIX には IFSunset の場合にデフォルト値と同じように振る舞うという規定がある。(2.6.5 Field Splitting参照)

IFS で結合できない

$ set -- a b c
$ IFS=:
$ echo "$*"
a b c

IFSfor のリストを単語分割してしまう

$ IFS=/
$ for i in a/b/c; do echo "$i"; done
a
b
c

POSIX に準拠している場合は単語分割しない

$ IFS=/
$ for i in a/b/c; do echo "$i"; done
a/b/c

! が使えない

$ if ! false; then
>   echo ok
> fi
!: not found

command コマンドがシェルビルトインではない

$ PATH=""
$ command echo "ok"
$ command: not found

シェル関数とシェル変数の名前空間が同じ

$ var() { echo "test"; }
var #=> test

$ export var
cannot export functions

$ var=123
$ var
var: not found

位置パラメータが $1$9 までしか使えない

$ echo "${10}"
bad substitution

shift してずらしていけば参照することはできる

. で読み込んだスクリプトを return で中断することができない

lib.sh
echo "loaded"
return
$ . ./lib.sh
loaded
cannot return when not in function

umask がシンボリックモードに対応していない

$ umask u=rwx,g=rx,o=rx
$ umask
0000
$ umask -S

alias が使えない

$ alias ECHO=echo
$ ECHO ok
ECHO: not found

Solaris 10 には外部コマンド版の alias (/usr/bin/alias) があるのでそれと勘違いしないように注意

変数代入が後ろから評価される

$ value=`echo a>&2` value=`echo b>&2` value=`echo c>&2`
c
b
a

パイプ記号として ^ が使えてしまう

$ echo "abc" ^ sed s/a/A/
Abc

Bourne シェルを使いたい場合

もし移植性のテストや古シェルスクリプトを動かしたいなどの理由で古い Bourne シェルを使いたい場合、以下の選択肢があります。

Heirloom Project

Heirloom Project の中の一つとして Heirloom Bourne Shell が提供されています。このプロジェクトは OpenSolaris で公開されたコードを元に基本的なコマンドを新しい OS で動くように移植しているプロジェクトです。シェルだけではなく各種コマンドの歴史的版(POSIX 以前)やその POSIX 版など当時のシェルスクリプトを動かすのに必要になる多数のコマンドが移植されています。また UTF-8 へ対応も行われているようです。積極的に開発をするたぐいのプロジェクトではありませんが 2007 年頃でメンテナンスは停止しているようです。ちなみに私は使ったことがありません。

Schily-Tools project

Schily-Tools project の中の一つとして Schily Bourne Shell が提供されています。Jörg Schilling さん(POSIX - Austin Group のメーリングリストでもよく見かける方です)によってメンテナンスされているプロジェクトでシェル以外にもいくつかのコマンドを提供しています。Schily Bourne Shell は OpenSolaris で公開された Bourne シェルをフォークして開発されており「The non-POSIX SVr4/OpenSolaris variant (obosh)」「The minimal POSIX compliant variant (pbosh)」「The POSIX compliant extended variant (bosh)」の 3 つの亜種が含まれています。この中で obosh が Bourne シェル相当のシェルです。プロジェクトは活発にメンテナンスされています。ちなみに私は POSIX シェルである bosh/pbosh を使うためにビルドしていますが obosh 自体はほとんど使っていません。

余談ですが Schily Bourne Shell の亜種の一つである pbosh は 最小限の POSIX 準拠シェルで POSIX で規定されていない機能を全く含んでいません。例えば dash は (おそらく実用上の理由で) POSIX では規定されていない local コマンドを例外的に実装していますが pbosh ではそれすらも実装していません。ただ Schily Bourne Shell はビルドが必要でユーザーも少ないため POSIX 準拠のシェルスクリプトの開発には dash をおすすめしています(yash の POSIX モードもおすすめです)。

さいごに

純粋な POSIX シェル用のシェルスクリプトを書きたい人は dash がおすすめです。インタラクティブシェルには向きませんが POSIX で規定された機能のみを実装する方針で一部例外を除き方言がほとんどなく採用事例が多いシェルです。Ubuntu / Debian のシステムシェル /bin/sh である他、macOS でもデフォルトでインストールされています。

他、なにか気がついたりしたら追記していくかもしれません。

参考文献 (この記事に書いてない違いがたくさん書かれています)

14
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9