シェルとは

本連載では、これからashまたはdashについて、その機能を説明していく。これらは/bin/shとしてデプロイされていることが多く、IEEE Std 1003.1(POSIX.1)の仕様にだいぶ近い。/bin/shの実装系がashやdashではなくbashだったとしても、bash自体がashやdashの機能を含んでいるため、ashやdashの機能を把握していればそのままbashでも使用できる。以降ではashやdashに相当する実装系を「sh」と呼ぶことにする。

shは「シェル」と呼ばれる種類のソフトウエアの1つだ。シェルは、ユーザーがログインしたときに最初に実行されるソフトウエアである。ユーザーから入力される行データを解釈し、命令を実行する。入力はユーザーからのみならず、ファイルから1行ずつデータを読み込んで処理することもできる。場合によって前者の使い方は「インタラクティブシェル」、後者の使い方は「シェルスクリプト」と呼ばれることもある。ユーザーが対話的に使用するシェルがインタラクティブシェルであり、ファイルに書き込まれたコマンドを実行していくのがシェルスクリプトだ。

シェルは、ユーザーがインタラクティブに操作することを補助するためにコマンド履歴機能や行編集機能を提供している。また、プログラミング言語的に利用するために制御構文やマクロ機能なども提供している。シェルはインタラクティブシェルとしても、シェルスクリプトとしても利用できるという特徴を備えている。この機能はPythonにも存在しているものだ。

なお、ユーザーのログイン時に実行されるシェルはchsh(1)コマンドなどを使うことで変更できる。

インタラクティブシェル

shは、シェルの標準入力が端末に接続されている場合、または-iオプションが指定されている場合に何も引数を指定しないで実行すると、インタラクティブシェルとして動作する。つまり、ユーザーが対話的にコマンドを入力して動作するモードとして振る舞うことになるわけだ。

シェルは、インタラクティブシェルとして動作する場合、ユーザーから入力を受ける場所の前部分にプロンプトと呼ばれる文字列を表示する。次の画面の場合、行頭に表示されている「$ 」がプロンプトである。

行頭の「$ 」がプロンプト。インタラクティブシェルとして動作しているsh

ユーザーがログインしたときにインタラクティブシェルとしてshが起動される場合、そのシェルはログインシェルと見なされる(引数0を調べ、この部分がダッシュになっているとログインシェルとなる。このシェルは、システム側がログイン時に自動的に指定することになる)。ログインシェルは/etc/profileを読み込むとともに、続けて~/.profileの内容を読み込む。このファイルには、shの設定や環境変数設定などが含まれており、デフォルトの挙動を決めることになる。

シェルスクリプト

shの最初のオプションではない引数はファイルへのパスと解釈され、そのファイルの内容を読み込んで処理するようになる。この場合、引数として渡されたファイルはシェルスクリプトと呼ばれるようになる。以下の場合、shに渡された「hello」はシェルスクリプトということになる。

$ cat hello
echo 'Hello World!'
$ sh ./hello
Hello World!
$

シェルスクリプトファイルの先頭に「#!/bin/sh」という文字列を書き込んでおくと、そのファイル単体でシェルスクリプトとして実行できるようになる。これはシェルの機能ではなくカーネルの機能で、カーネルはファイルの先頭が「#!/bin/sh」のファイルを実行する場合、「/bin/sh そのファイルのパス」のように実行する。つまり、カーネルが前述した実行処理を代替して行ってくれるわけである。

$ cat hello.sh
#!/bin/sh

echo 'Hello World!'
$ ls -l hello.sh
-rwxr-xr-x  1 daichi  staff  31  6月 11 10:46 hello.sh
$ ./hello.sh
Hello World!
$

シェルスクリプトでファイルパス以降に指定された引数は、位置パラメータ($1、$2、$3 …)としてアクセスできる。

このようにインタラクティブシェル、またはシェルスクリプトとして実行するというのがシェルの基本的な2つの使い方だ。

コマンド引数

shはオプションを指定することで挙動を変えることができる。指定できるオプションは実装系ごとに異なるが、大体共通で用意されているオプションをまとめると次のようになる。

オプション 内容
-a 起動時に全ての環境変数を引き継ぐ
-C 既存のファイルを>で上書きしない
-c 文字列 コマンドを指定された文字列から読み取る
-e シェルスクリプトとして動作している場合、コマンドが失敗した場合に即時にシェルを終了する。ただし、if、elif、while、until、&&、||、!などのキーワードと合わせて使われている場合にはその限りではない
-f パス名展開を無効化
-I インタラクティブシェルとして動作している場合、入力からのEOFを無視
-i インタラクティブシェルとして動作させる
-p 特権モードを有効化
-s コマンドを標準入力から読み取る
-v 標準入力から読み込んだデータを標準エラー出力へ書き出し(デバッグ向け機能)
-x 各コマンドを実行する前に標準エラー出力へ書き出し(デバッグ向け機能)

なかでもよく使うのは「-c」と「-s」、それに「-e」「 -v」「 -x」の組み合わせだ。-cや-sはそれぞれコマンドを読み込む先を指定するオプションである。-cは実行するコマンドを直接引数に文字列として指定し、-sは実行するコマンドを標準入力から取得するという指定になる。

●「sh -c」の実行サンプル

$ sh -c "echo Hello World!"
Hello World!
$

●「sh -s」の実行サンプル

$ echo "echo Hello World!" | sh -s
Hello World!
$

あまり知られていないようだが、シェルにはデバッグに使用できるオプションとして「-e」「-v」「-x」が用意されている。-eはコマンドが失敗した場合に即座にシェルを終了するオプション、-vは入力時にコマンドラインを表示するコマンド、-xは実行前にコマンドラインを表示するコマンドだ。また、-vと-xの組み合わせでは、展開処理前後にどのようなコマンドが実行されるのかを知ることができる。 -e、-v、-xの利用例は次の通りだ。

% sh -e -v -x
$ echo $(date)
echo $(date)
+ date
+ echo $'2019年' $'6月11日' $'火曜日' $'11時32分56秒' JST
2019年 6月11日 火曜日 11時32分56秒 JST
$ true
true
+ true
$ false
false
+ false
%

シェルを学び始めたばかりの段階では、これらのオプションを使うことはほとんどないだろう。ある程度シェルが使えるようになったら、この辺りのオプションを思い出して利用できるようになればよいだろう。

インタラクティブシェルとシェルスクリプト

シェルはユーザーが毎日対話的に使用するソフトウエアであるとともに、ファイルに書き込んで使用するバッチ処理向けのソフトウエアとしても利用される。Linuxをはじめ、UNIX系のソフトウエアはシステムの多くをシェルスクリプトで組んでいる。シェルスクリプトを読めるようになることは、こうしたシステムの動作を理解する上でも大切なことだ。

また、シェルの操作スキルの向上は、そのままLinux操作のスキル向上や効率性の向上につながる。慣れないうちはきついかもしれないが、慣れてさえしまえば大きな技術財産となるものだ。将来楽をするために、今はちまちまとシェルの機能を覚えていこう。

参考資料