前回
シェルってなに?コマンドラインインタプリタってなに? - シェルもどきを自作する#1
に引き続き、「シェルとは何か?コマンドラインインタプリタとは何か?」について「シェルもどき(oreshell)」をgoで作りながら学ぶ。
前回作った原始的なシェルではいくつか期待しない動作が起きた。
今回はそのうちの以下の事象について対応する。
現状の実装ではユーザが入力した文字列をそのまま全部一つの文字列としてOSカーネルに渡してプログラムの起動を要求している。
正しくは、コマンド部分と引数部分を分解して渡す必要がある。(go os.StartProcess())
□修正前
□修正後
ユーザが入力した文字列を、空白毎に分解している。
現時点ではユーザが入力した文字列を単純に空白毎に分解したが、この「シェルもどき」を実用的なシェルするためにはもっと複雑な構文に対応する必要がある。
もっと複雑な構文に対応するためには、字句解析、構文解析を実装する必要があるが、いまはそこまで考えない。
環境変数とは。
(wikipediaより)
環境変数(かんきょうへんすう、英語: environment variable)はオペレーティングシステム (OS) が提供するデータ共有機能の一つ。
OS上で動作するタスク(プロセス)がデータを共有するための仕組みである。
特にタスクに対して外部からデータを与え、タスクの挙動・設定を変更するために用いる。
すべてのプロセスは環境変数を持つ。
環境変数はキーと値のペアで構成する。
環境変数の例としてPATHがある。
(atmarkITの記事より)
コマンドサーチパス(PATH)って何?
「コマンドファイルをパス付きで指定する」ということは、例えば「ls」コマンドならば「/bin/ls」と入力する、ということになります。 しかし、よく使うコマンドを毎回パス付きで指定するのは非常に面倒です。
Linuxでは、「コマンドサーチパス(コマンド検索パス)に登録してあるディレクトリにあるファイルは、パス名を省略してよい」という決まりになっています。「パス」だけで、このコマンドサーチパスを指すこともあります。
コマンドサーチパスは、「PATH」という名前の「環境変数」に保存されています。
プログラムを起動する場合は、そのプログラムの場所(絶対パス)を知る必要がある。
シェル、またはOSカーネルは、ユーザが指定した文字列を以下の表の通り絶対パスに変換したあと、プログラムの起動を試みる。
ユーザが指定したコマンド文字列 | 絶対パスにするための処理 |
---|---|
絶対パス | (そのまま) |
相対パス | シェルの現在のカレントディレクトリと合成して絶対パスに変換する。 |
プログラムファイル名 | PATHに登録されたパス群からプログラムファイル名を探し、見つかったらそのパスとプログラムファイル名を合成して絶対パスに変換する。 |
このパスの合成/PATHからの探索を、自身でやっているシェルもあるし、OSカーネルにやらせているシェルもある(っぽい)。
(OSカーネルにやらせる … linuxの場合はexecvp()、execlp()を使う。 -> wikipedia exec)
「シェルもどき」では勉強のためにシェル自身でやることにした。
シェルで実行するコマンドは
の2つに分類できる。
外部コマンドはOSカーネルが起動するプログラムだが、内部コマンドはシェル自身が持つ機能である。
外部コマンドの例。
内部コマンドの例。
内部コマンドにはどんなものがあるか。
例えば代表的なシェルのBashの内部コマンドの一覧は「help」で確認できる。
シェルは、入力された文字列が
「シェルもどき」は現時点では2つの内部コマンド「cd」「exit」に対応した。
HashMapであるinternalCommandsに
を登録している。
ユーザが入力した文字列を空白毎に分解し、先頭の単語(コマンド名)が内部コマンド名である場合は、該当処理関数を実行する。
そうでない場合は、OSカーネルに該当するプログラムの起動を要求する。
□内部コマンド cd
引数がない場合は、ホームディレクトリにカレントディレクトリを移動する。
引数が1つある場合は、そのディレクトリに移動する。
引数が2つ以上の場合は、エラー表示する。
□内部コマンド exit
os.Exit()でシェルを終了する。
□外部コマンドを実行する部分
今まではmain関数内部にあったが、今回は関数化してmain関数外部に追い出した。