こんにちは、R.Kです。
普段はデータベースエンジニアをやっています。
今回は「bash」についてお話ししたいと思います。
1. はじめに
2. OSの基本要素
2.2 カーネル
2.3 シェル
3. Bashとは
3.1 シェルとBashの関係
3.2 補足:シェルの種類と歴史
4. コマンドの仕組みと環境変数
4.1 コマンドとは
4.2 コマンドの種類
4.3 環境変数とは
4.4 補足:Bashでの環境変数の設定方法
5. 最後に
1. はじめに
普段なんとなくで使っている「bash」。皆様はどういったものかご存じでしょうか?
今回、「シェル」と「カーネル」、「シェル」と「bash」の関係性についてまとめさせていただきました。
まずは、OSの構造について軽く説明いたします。
OSの構造を簡単にまとめると、下記の2つの要素で成り立っています。
・カーネル
・シェル
「カーネル」とはOSの中心となる「OSが持つ機能の集合体(核)」で、カーネルとユーザ(人間)の仲介役としてカーネルを囲うように「シェル」が存在します。
シェルを介して我々ユーザはカーネルに命令をして、コンピュータは動いています。
2. OSの基本要素
続きまして、上記で紹介した「カーネル」と「シェル」について、詳しく紹介したいと思います。
2.1 カーネル
「カーネル」は日本語では「核」と訳され、文字通りOSの要になります。
カーネルの役割は「物理装置の抽象化」です。
物理層とCPUやメモリなどの、コンピュータを構成するパーツのことです。これを「ハードウェア」と言います。ハードウェアは「メーカー」や「型番」、「製造ロット」などで「個体差」が生じ、同一規格製品でも細部が異なり「全く同じ環境」というものを再現することができません。
そのため、極端な話になりますが、あるプログラムを実行したときに以下のような結果になってしまう場合があります。
・コンピュータAでは「ANSWER_A」という結果が得られた
・コンピュータBでは「ANSWER_B」という結果が得られた
・コンピュータCではそもそもプログラムを実行できなかった
これを防ぐためにはコンピュータごとに調整したプログラムを用意しないといけなくなり、互換性や機能性で不利が生じます。
この、「コンピュータごとの差異」をなくし、標準化するのが「OS」の役割であり、「カーネル」が持つ一番大切な機能です。
「i7、Xeon、RyzenでもCPUはCPUというハードウェア」を「個別の製品を1つの部品名称に抽象化」し、ユーザ側に考えさせないようにするのがカーネルの役割です。
以下のことはすべて「カーネル」が自動で管理、運用してくれています。
・CPUやメモリのアクセス管理
・ディスクへの読み書き
・ネットワーク通信
etc...
Tips:
「カーネル」が処理できなくなり破綻することを、Linuxだと「カーネルパニック(Kernel Panic)」、Windowsだと「ブルースクリーン(Blue Screen Of Death)」と言います。
2.2 シェル
コンピュータは「カーネル」によって制御されていることを述べさせていただきましたが、「カーネル」には「命令待機をし、命令を受諾して実行する」機能、つまり、「ユーザ(人間)側から命令を送受信する機能」はありません。
この機能を担っているのが「シェル」です。
「シェル」の日本語では「貝殻」と訳され、カーネル(核)を覆いとのやり取りを仲介するという意味が込められています。
カーネルは複数のプログラムの集合体です。
「CPUを制御するプログラム」、「メモリを操作するプログラム」、「IOを管理するプログラム」など、機能ごとに分かれたプログラムが相互に連携を取り合い、コンピュータを動作させています。
このままでは「1+1を計算する」だけでもCPUとメモリをつかさどる各プログラムに1つ1つ命令を下す必要があります。
「シェル」の役割は、「カーネルをさらに抽象化し、1つの「コンピュータ」という機械にする」ことです。
シェルはユーザからの複雑な命令を「コマンド」という単位に集約し、カーネルを通して「命令を解釈して実行」、結果を返すのが主な機能です。
「1+1を計算する」という命令を例にとると以下のようになります。
1. 「計算を実行する命令(コマンド)」と「1」「+」「1」の文字をユーザから受け取る
2. カーネルのプログラム群が計算を行い「2」という結果が得られる
3. 「2」という結果を、ユーザに文字として画面に表示する。
「カーネル」という抽象化されたものをさらに抽象化し、「1+1は?」と聞くだけで「2」を返してくれるようになりました。
このように、カーネルとシェルは「コンピュータというパーツの集合体」を「コンピュータという1つの機械に抽象化」してくれることで、私たちユーザは様々なことをコンピュータで実行できるのです。
3. Bashとは
ここからは「Bash」について紹介します。
カーネルは同一規格として販売されているハードウェアの抽象化を行うため、ある程度決まったものが用意されています。
ただし、その中身のプログラムの書き方(実装方法)や管理の考え方の違いによって差異が生じ、違うOSとなります。
「Linux」や「Windows」の大きな違いが生じるのは、この「カーネル」が違うためです。これを「Linuxカーネル」、「Windowsカーネル」と言います。
「Linuxカーネル」を使用したOS同士は「核」が同じなので互換性があります。
ただし、Linux OSを作成している団体(ディストリビューターと言います)によって使用される補助プログラムやバージョンが違います。
シェルもその1つで、実装されるバージョンや操作方法が違うプログラムが複数用意され、基本的にはユーザ側が選択できるようになっています。
「Bash」はその実装の1つになります。
3.1 Bourne Again shell(bash)
1988年に「GNUプロジェクト」という、オープンソースソフトウェア(OSS)プロジェクトで開発された、近年ではUNIX系OSの「デファクトスタンダード」となったシェルプログラムです。
Linuxの開発者「リーナス・トーバルズ」がLinuxに搭載したことで今日のシェアにつながりました。
「Bourne Shell(sh)」というUNIX OSに搭載されていたシェルプログラムをOSSとして再現し、「Bourne Shell(sh)」より多機能かつ高性能になったシェルプログラムになります。
そのため、名前に「Bourne shell Again(shの再来)」と「Born Again(新生)」という意味を込めて、「Bourne Again shell」、略して「bash」と呼ばれるようになりました。
現在のシェルプログラムとして必要な機能を備えております。
1. ジョブ管理機能(fg/bg)
2. コマンドの実行を停止/再開(Ctrl+C、Ctrl+Z)
3. 履歴(historyコマンド
4. ホームディレクトリ(~)の定義
5. エイリアス機能(aliasコマンド)
6. シェル上で四則演算
7. ヒアドキュメント(<< EOF)
etc...
後ほど、歴史として紹介しますが、「bash」は、多機能であるがOSSではなかった「Korn Shell」の機能をOSSで実現するために作り出されたシェルプログラムです。
Tips:
「GNUプロジェクト」は、当時サーバー用OSとして普及していた高性能ですが高価だった「UNIX」を「1からオープンソースソフトウェアとして開発する」を目的としたUNIX互換OS開発プロジェクトです。現在でも開発活動が行われています。
残念ながら独自カーネルの作成は活動停止状態ですが、カーネルを開発中に開発されたプログラム群(bash、grep、tarなど)がLinux側に導入され、現在のLinuxへと引き継がれています。
3.2 補足:シェルの種類と歴史
ここでは軽く、「bash」が生まれるまでに開発された、「bash」の先祖について歴史と共に紹介します。
●Bourne Shell(sh)
現在、多くのLinuxディストリビューションで使用されている「bash」、その原型です。1977年にスティーブ・ボーンさんという方がベル研究所製UNIX OS「Unix Version 7」向けに開発しました。
今日のシェル機能の基本機能が搭載されてました。
●C shell(csh)
1978年にビル・ジョイさんという方が開発したシェル。「C言語」風なシェル用言語でシェルスクリプトが書けます。
カルフォルニア大学バークレー校製UNIX OS「BSD Unix」のデフォルトシェルとして開発されました。
ジョブ管理機能が搭載され、コマンドの実行を停止/再開できるようになりました。また、履歴(history)、ホームディレクトリ(~)、エイリアス機能などの「sh」に無かった便利機能も追加されました。
ただ、シェル用言語が特殊なため「sh」とシェルスクリプトの互換性がなくなりました。
●Korn Shell(ksh)
1983年にデビッド・コーンさんという方さんという方が開発したシェル。
「sh」のシェル用言語との互換性を持ちながら「csh」の便利機能とジョブ管理機能を兼ね備えた、「sh」と「csh」のいいとこどりなシェルです。
ただ長らく(2000年まで)ベル研究所が所有するプロプライエタリでソースが公開されてなかったため、LinuxなどのUNIX系OSには搭載できませんでした。
上記のように今でも使えるシェルプログラムはいくつかありパッケージとして配布されています。よって、RHEL系ではrpm/dnfコマンド、Debian系ではdpkg/aptコマンドから導入可能です。
ただ、「bash」の使い勝手の良さから、インストール時に導入されるのは「bash」になります。
4. コマンドの仕組みと環境変数
ここからは、実際にシェルを介してカーネルへの命令を送る方法、その実装方法である「コマンドライン」について述べたいと思います。
4.1 コマンド(コマンドライン)とは
コマンドとは、英語で「命令、指揮、指令」という意味を持つ英単語です。コマンドラインは「コマンド文章」という意味になります。
単に「コマンド」と呼ばれますが、実際の使用時にはコマンドに「引数」を設定して実行することが多いので「コマンドライン」と呼ぶのが正しいです。
Linux/Windows OSともに、実行できるプログラムは「実行可能ファイル」と呼ばれます。その「実行可能ファイル」の「ファイル名」を「コマンド」ということが多いです。
Linuxのコマンド(プログラム)の1つ「date」を例とすると、「date」コマンドは「/usr/bin/date」という実行可能ファイルへの短縮パスになります。
シェル(bashを例にする)に「date」を打ち込んでエンターを押下すると
1. ユーザがbashに「date」という文字列(コマンドライン)を打ち込み、エンターを押しbashに読み込ませる
2. シェルは「PATH」という名前で定義された「環境変数」を見て指定されたディレクトリの内容を取得
3. 「date」と名付けられた「実行可能ファイル」を検索
4. 「date」ファイルを発見したらそのファイルの内容を実行
5. 「実行時点での現在時刻」という結果が得られたのでシェルにその結果を戻す
6. シェルは戻された結果を画面に表示してユーザに提供する
これが「コマンド実行」という一連の過程のです。
今回は簡略化するため、引数を割愛してます。
4.2 コマンドの種類
上記で「コマンドの実態は「実行可能ファイル」である」と述べましたが例外があります。「内部コマンド/ビルトインコマンド」と呼ばれる、「シェルプログラムに組み込まれた「実行可能ファイル」ではないコマンド」呼ばれるものです。
内部コマンドと区別するため、コンピューター用語で「実態が「実行可能ファイル」」を「外部コマンド」と言います。
基本的にプログラムは独立して存在しておりますが、「シェルプログラム自体が処理を行わないと対応できないもの」が存在します。
例えばディレクトリの内容を表示する「ls」コマンド。これはシェルプログラムがディレクトリの情報を、「ファイルシステム」というカーネルの機能から取得して表示するコマンドになります。
このように「外部コマンドに委託できない機能」、もしくは、「シェル自体が持つべき機能」はシェルが処理します。「実行可能ファイルを呼び出すのではなく自分内部にある機能を呼び出す」ので、「内部コマンド」と呼ばれます。
シェルプログラムの機能のため、「csh」やWindowsの「cmd/PowerShell」にもビルトインコマンドは用意されています(「dir」などが該当します)。
bashの一例を以下に記載します。(実際には50個以上あります)
Tips:
「内部コマンド」は実行可能ファイルは必要ありませんが、「/usr/bin」ディレクトリ内に「cd」や「ls」といった「実行可能ファイル」が用意されています。
これは「POSIX規格(別Tips参照)に適応するため」です。
詳細は省きますがPOSIX規格のは「すべてのコマンドは内部コマンドの"exec"(別Tips参照)で実行できる必要がある」という仕様が存在します。
この「すべてのコマンド」とは「内部コマンド」も含まれており、「内部コマンドのexec」で「内部コマンド」を実行できないといけない」=「自分(bash)で自分(bash)を呼び出せないといけない」
という機能を実装しないといけません。
これを成立するために「execコマンドで実行できる、「内部コマンド」を呼び出すだけの実行可能ファイル」が「/usr/bin」に用意されています。
(内部コマンド"exec"で外部コマンド"/usr/bin/ls"を実行し、外部コマンド"/usr/bin/ls"が内部コマンド"ls"を実行することで、この規格に適合させました。)
Tips:
「POSIX」は「Portable Operating System Interface」の略で、「UNIX系OS(Linux、UNIX、BSDなど)間で互換性と移植性を保証するための規格」です。
Tips:
execコマンドは「外部コマンドを呼び出す内部コマンド」です。
コマンドを実行すると通常は新たなプロセスとして起動しますが、プロセスをその外部コマンドに譲渡したい(新たにプロセスを作りたくない)場合に使用します。制限として「内部コマンド」は呼び出せません。
(自分のプロセスを自分に譲渡するという、矛盾が発生するため)
4.3 環境変数
最後に「環境変数」について述べさせていただきます。
上記コマンドの説明で「環境変数」という言葉が出てきたかと認識されているかと思います。
「環境変数」とは「環境ごと、筐体(マシン)ごと、OSごとに独自で定義した/されている変数」になります。環境変数は使用状況によって、ユーザが定義することによっても変わり、共通で定義/用意されているもの以外はバラバラな場合が多いです。
環境変数は以下のように設定されます。
「環境変数名」=「環境変数の値」
Oracle Databaseで使われる環境変数名「ORACLE_HOME」で考えてみます。
環境変数名「ORACLE_HOME」自体はOracle Databaseに用意(名前付け)された変数で、Oracle製品のバイナリインストールディレクトリを指し示す環境変数になります。
インストールディレクトリのため、製品のインストール場所によって「環境変数」が変化します。
例としてOracle DatabaseとOracle Grid Infrastructureの2種を1つのマシンにインストールした場合を想定すると以下のようになります。
例1-1/ Oracle Database
ORACLE_HOME=/u01/app/oracle/product/19.0.0.0/dbhome_1例1-2/ Oracle Grid Infrastoructure
ORACLE_HOME=/u01/app/19.0.0.0/grid
環境変数名「ORACLE_HOME」を使った別の例としては「バージョン」が違う場合です。Oracle database 19cと23cを1つのマシンにインストールした場合を想定すると以下のようになります。
例2-1/ Oracle Database 19c
ORACLE_HOME=/u01/app/oracle/product/19.0.0.0/dbhome_1例2-2/ Oracle Database 23c
ORACLE_HOME=/u01/app/oracle/product/23.0.0.0/dbhome_1
このように、環境によって同じ環境変数名でも設定された値が違い、別の結果が返ってくる、よって「環境変数」と呼ばれています。
Tips:
「変数」は英語で「variable」といい「var」と略されます。
Tips:
コマンドを実行する過程で「PATH」という環境変数が出てきました。「道」という意味の英語です。
「PATH」は「外部コマンド(実行可能ファイル)が保管されているディレクトリを定義したもの」です。シェルはこの「PATH」に書かれたディレクトリを順に辿って、指定されたコマンドを検索しに行きます。例:
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin「:」が区切り文字でコマンド検索時は「/bin」、「/sbin」、「/usr/bin」、「/usr/sbin」、「/usr/local/bin」、「/usr/local/sbin」が参照されます。
4.4 補足:Bashでの環境変数の設定方法
Bashで環境変数を設定する場合には内部コマンド"export"を使用します。
設定した環境変数を呼び出す場合には、設定時の環境変数名の前に「$」を入れることで呼び出せます。
「ORACLE_HOME」の場合「$ORACLE_HOME」と打ち込むことで呼び出せます。
例:
bash 4.41:~# export TEST_VER="これはテストです"
bash 4.41:~# echo $TEST_VER
これはテストです
このように日本語を設定することも可能です。
5. 最後に
今回、自分の勉強も兼ねてBashやシェル、カーネルのことをまとめさせていただきました。まだまだ、表層部分を少し覗いただけなので突き詰めると奥の深い泥濘のような世界が広がっていることを感じました。
ここまでくると「カーネル」のことや「OS全体のこと」も勉強したいと思ってます。
が、記載内容が増えそうなので、ここで筆をおかせていただきます。
以上、「シェルとカーネルとは-bashといったいなにか-」についてでした。
お読みくださってありがとうございました。