シェルってなぁに
〜 内臓を保護するプログラム 〜
2024-11-11 作成 福島
TOP > shell > whatisshell
[ TIPS | TOYS | OTAKU | LINK | MOVIE | CGI | AvTitle | ConfuTerm | HIST | AnSt | Asob | Shell ]

1. まともな OS には階層がある

コンピュータに触れるとき、ユーザの相手をしてくれるのがシェル (ログインシェル) です。
実はコンピュータの OS はいくつかの階層に分かれて作られており、高度な OS であるほど階層が多くなっています。
OS の簡易階層図
ユーザユーザがアプリケーション*1を通してコンピュータを操作する
アプリケーションユーザが作成したプログラム。プログラミング言語・ゲーム・ワープロ・Web サーバ 等 ← シェルはここ
OSライブラリアプリケーションから発行される命令に従って動作する便利プログラムの集合体
カーネル短い間隔 (1/100秒以下) で常にハードウェアを監視し、他から要求があればハードウェアを操作する
ハードウェアCPU・メモリ・ハードディスク・時計・ディスプレイ 等
*1「アプリケーションソフトウェア」の略。最近はさらに短縮されて「アプリ」と呼ばれる。
ユーザと OS の間にアプリケーションが存在し、シェルはアプリケーションとして動作します。
ユーザが操作するのはアプリケーションであり、直接に OS やハードウェアを操作することはありません。
もし OS やハードウェアを操作する必要があれば、アプリケーションやライブラリのプログラムを作ることになります。
2.「OS ではない OS」と「OS な OS」がある
日常的に Linux を使っていますが、これを OS と呼んでいると思います。
屁理屈的なことを言うと、これは「ディストリビューション」(意味: 配布物) であり、OS ではありません。

Linux とは上記簡易階層図の「カーネル」部であり、ライブラリやアプリケーションが同梱され「ディストリビューション」となって配布されています。
OS を便利に使おうとすると様々なソフトウェアが必要になり、それぞれの作者には当然の主張があるため、こうならざるを得ません。

Linux ディストリビューションの例 (ほかにもたくさんあります)
一方、Solaris は OS です。オラクルという会社が販売しています。
オープンソースの OpenIndiana (OpenSoaris の後継) もあり、こちらはディストリビューションです。
3. シェルは内臓を隠すもの
シェルにもいろいろありますが、多くの人は「便利なツール」「よくわからないけど必要なもの」という認識だと思います。
その認識はだいたい合っています。

シェル (Shell: 貝殻) はなぜそう呼ばれているかというと「脆弱な内臓を覆い隠す」という意味があるからだそうです。
ユーザがコンピュータを操作するとき、カーネルやハードウェアを直接操作すると OS 内部の知識が必要なだけでなく、
本来の目的を果たすために多くの手順を踏まなければならない上に、少しでも間違えると意図しない悪影響を引き起こします。

そんなことをさせないよう、ユーザがやりたいことを簡素かつ直感的にコンピュータに伝えるための道具がシェルです。
ファイルをコピーしたいだけなのに、いくつものシステムコールやデバイス番号、必要なバッファの計算等を逐一考えたくないのです。
OS という内臓を覆い隠すことにより利便性を向上させているため、初めて触れる人にとって「よくわからない」のは当然です。

シェルに対しての「よくわからない」が「よくわかった」になったとき、それはシェルを理解しただけでなく OS も理解したことになります。

シェル自体がどんどん高機能になって「便利なツール」になっています。
多くの Linux ディストリビューションの標準シェルは Bash です。(Solaris11, OpenIndiana も Bash)
4. REPL って何?
シェルは REPL(リプル) とも呼ばれています。
REPL は本来、インタプリタ (プログラミング言語) のひとつの機能の呼び方です。(由来は Lisp 言語から)
1. Read … 読み込み
2. Eval … 評価
3. Print … 出力
Loop … 繰り返し
の頭文字を並べたもので 1,2,3 を順に実行し、これを繰り返します。
1. ユーザからの命令を受け付け、
2. 受け付けた命令を実行し、
3. 実行結果をユーザに返す。
…を永遠に繰り返す、という意味です。
すべてのシェルをインタプリタとみなして良いかどうかはわかりませんが、
少なくとも Bash はスクリプトを使ったバッチ処理 (一括処理) が可能なうえ、
プログラミング言語の一覧にも掲載されているので、REPL と呼んでも構わないでしょう。

REPL の機能を有するプログラミング言語なら、シェルとして動作することができます。
(REPL の機能がないプログラミング言語も沢山あります)
5. ここはどこ?私は誰?
OS にユーザ ID とパスワードを打ち込んでログインすると、出迎えてくれるのがシェルです。これをログインシェルと呼びます。
自分のログインシェルは何になっているでしょうか?管理者に嫌われてなければ Bash になっていると思います。
(Linux はかなり昔から Bash。Solaris は 11 でユーザシェルが Bash になりました)

ログイン直後に以下を実行し、その下の実行結果と同じなら Bash になっているでしょう。*2
$ echo $0
bash       

$ echo $SHELL
/bin/bash  

*2この変数は Bash 自体が名乗っているため、実はあてになりません。
Bash 以外で ps のようなプログラムを呼び出せば確実ですが、OS の知識が必要となるのでここでは割愛します。
打つのが面倒だけど、こうやっても確認できます。(他人のも確認できます)
$ cat /etc/passwd | grep '自分のユーザID' | cut -d ':' -f 7

/bin/bash  
6. ログインシェルは Bash だけ?
一番有名なログインシェルは Bash です。ほかにも sh(Bourne Shell)、csh(tcsh)、zsh 等があります。
文法が異なりますが概念はほぼ一緒なので、これらに変更しても戸惑うことはあまりないでしょう。
(概念が一緒なら、変更する必要がないともいえる)

REPL の機能があれば何でもログインシェルにできるため、
(変態と呼ばれても構わないなら) Python や Node.js をログインシェルに指定することもできます。

• Python をログインシェルにした場合 (ちょっと変態)
$ cat /etc/passwd | grep 'python-mania' | cut -d ':' -f 7

/bin/python  

ユーザ python-mania としてログイン (ログアウトは + をタイプする)
Python 3.9.18 (main, Jan  4 2024, 00:00:00)
[GCC 11.4.1 20230605 (Red Hat 11.4.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.  
>>> print('Hello, Python')
Hello, Python
>>>

組み込み用マイコンボードの Raspberry Pi Pico では推奨シェル (REPL) が Python (MicroPython) になっています。
• Node.js (JavaScript) をログインシェルにした場合 (まだ人間に戻れる)
$ cat /etc/passwd | grep 'js-mania' | cut -d ':' -f 7

/bin/node    

ユーザ js-mania としてログイン
Welcome to Node.js v16.20.2.
Type ".help" for more information.                                      
> console.log('Hello, Node.js')
Hello, Node.js
undefined
> .exit
• PHP をログインシェルにした場合 (そろそろヤバい)
世間では PsySH が話題のようだが、ここは敢えて PHP のインタラクティブモード。(せめて EPEL に入ったら考える)
$ su

# echo '#!/bin/php -a' > /bin/php-a

# chmod +x /bin/php-a

# exit

$ cat /etc/passwd | grep 'php-mania' | cut -d ':' -f 7

/bin/php-a 

ユーザ php-mania としてログイン
Last login: Fri Nov  1 15:16:13 2024 from 192.168.1.11                  
Interactive shell

php > echo "Hello, PHP\n";
Hello, PHP
php > exit
• ド変態 (賞賛) の場合 (ああ、何ということだぁ…)
$ cat /etc/passwd | grep 'pl-mania' | cut -d ':' -f 7

/bin/gprolog 

ユーザ pl-mania としてログイン
GNU Prolog 1.5.0 (64 bits)                                              
Compiled Jul 21 2022, 00:00:00 with gcc
Copyright (C) 1999-2021 Daniel Diaz

| ?- write('Hello, Prolog').
Hello, Prolog

yes
| ?- system('pwd').   % No, you should love solitude.
/home/pl-mania

yes
| ?- halt.
7. アプリケーションを起動するアプリケーション
Bash は非常に偉い誰かが作ったアプリケーションです。
非常に有用で便利ですが、すべての機能があるわけではありません。
例えば C コンパイラや Web サーバの機能は Bash にありません。

C コンパイラや Web サーバは複雑で巨大なアプリケーションなので、これを使用しないユーザにとっては邪魔になります。

Bash は、自分を無駄に高機能にしない代わりに、ほかのアプリケーションを起動させることができます。
その時々に応じてエディタやコンパイラ、その他 (Python 等) のインタプリタ等を呼び出すことにより、複雑な動作を実現します。

例えば、ファイルを送受信するなら ftp や scp、wget コマンド (命令) を、
他のサーバで作業をこなしたい (リモートジョブ) なら ssh や telnet コマンドを、
シェルから適宜に呼び出します。
例: www.example.jp と news.example.jp の Web ページを取得するスクリプト。
ファイル名: web-get-sample.sh
#!/usr/bin/bash
wget http://www.example.jp/  -O toppage.html
wget http://news.example.jp/ -O newspage.html
wget コマンドを 2 回呼び出している。


Bash もアプリケーションのひとつなので、Bash から Bash を起動させることができます。
ファイルや標準入力を指定すればインタプリタとして動作し、指定しなければ REPL として動作します。

• スクリプトファイルを作成して Bash に実行させた場合 (インタプリタとして実行)
$ cat > ./example.sh << EOF
A=1
B=2
echo \`expr \$A + \$B\`
EOF

$ cat ./example.sh
A=1
B=2
echo `expr $A + $B`  

$ bash ./example.sh
 3 
• 標準入力から Bash へスクリプトを送り込んだ場合 (インタプリタとして実行)
$ echo 'A=1 ; B=2 ; echo `expr $A + $B`' | bash
 3 
• 何も指定せず Bash を起動した場合 (REPL として実行)
$ bash
$ A=1
$ B=2
$ echo `expr $A + $B`  
3
$ 
これはログインした状態と同じ。
8. 直前に実行したコマンドをシェルスクリプトにしてみる
「Linux のコマンドはずいぶん使えるようになったけど、スクリプトはいまいち…」
…という人が居るかどうか知らないけど、Bash には history コマンドがあります。
これを使って、実行したコマンドをスクリプトファイルにできます。

8-1. 必要なコマンドを普通に実行する。(ここでは、必要ないコマンドも混ぜてます)
$ # カンケーないコマンド
$ A=1
$ B=2
$ echo `expr $A + $B`
3
$ # カンケーないコマンド
8-2. history コマンド
Bash に組み込まれている history コマンドは、過去に自分が実行したコマンドを表示します。
オプションに数値を指定すると、直近の数行だけを表示します。

ここで直近の 5 行を指定して history コマンドを実行すると、以下になります。

$ history 5
11  # カンケーないコマンド  
12  A=1
13  B=2
14  echo `expr $A + $B`
15  # カンケーないコマンド
行頭の数字は、実行されたコマンドの順番を示しています。
8-3. コマンドだけを取得する。
行頭の数値が邪魔なので削除すると、実行したコマンドを得られます。
('
 
' は空白文字です。分かりにくいので色をつけています)


$ history 5 | cut -d ' ' -f 3-

# カンケーないコマンド  
A=1
B=2
echo `expr $A + $B`
# カンケーないコマンド
実はちょっと嘘が入ってる: すでに上記 8-2 で history を実行しているため、本当は行数がズレる。
8-4. スクリプトファイルを作る。
上記 8-3 をファイルにリダイレクトしてテキストエディタで成形すれば、スクリプトファイルの完成。

$ history 5 | cut -d ' ' -f 3- > ./example.sh

$ vim ./example.sh    # ← 必要ないコマンドをテキストエディタで削除する。


$ cat ./example.sh

A=1
B=2
echo `expr $A + $B`

実行してみる

$ bash ./example.sh

 3