# シェルってなに?コマンドラインインタプリタってなに? - シェルもどきをgoで自作する#1 ## はじめに 「シェルとは何か?コマンドラインインタプリタとは何か?」について「シェルもどき([oreshell](https://github.com/jyami/oreshell))」をgoで作りながら学ぶ。 ## 「・・・ってなに?」 ### シェルってなに? [wikipediaより](https://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%A7%E3%83%AB) > シェル (英語: shell) はオペレーティングシステム(OS)があるような高機能なコンピュータにおいて、ユーザーのためにインタフェースを提供するコンピュータプログラムであり、オペレーティングシステムが提供する機能へのアクセスを提供する。(略) > コンピュータシステムとユーザーの間にある「殻」であることから、このように呼ばれる。 > 大まかに2種類に大別すると、キャラクタユーザインタフェース(CUI)ベースのコマンドラインインタプリタ(CLI)の形態をとる「コマンドラインシェル」と、グラフィカルユーザインタフェース(GUI)ベースでいわゆる「デスクトップメタファ」等の環境へのユーザインタフェースという形態をとる「グラフィカルシェル」に分けられる。 Unix/Linuxの場合、シェルを説明するときによく使われる図は以下の通り。 ![](https://i.imgur.com/OpzOQ0m.png) OS利用者はOSの中核(カーネル)に対して、操作/要求を直接送ることはできない。 シェルを通じてカーネルに操作/要求を送る。 - シェルには以下の2種類がある - GUIシェル - CUIシェル (今回の話はこちらに限定) - unix/linuxのシェルの例 - bash - sh - zsh - etc... - unix/linux以外のシェルの例 - windows の cmd.exe - windowsのpowershell - etc... - プログラミング言語のシェル ### コマンドラインインタプリタってなに? [wikipediaより](https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%97%E3%83%AA%E3%82%BF) > コマンドラインインタプリタ (英: command line interpreter) は、コンピュータのコマンドライン、すなわち、ユーザによりキーボードなどから入力される文字列の行(ライン)のコマンドを解釈し、オペレーティングシステム(以下、OS)やプログラミング言語処理系などに渡す、コマンドラインインタフェースであり、キャラクタユーザインタフェースを構成する要素である。 > OSの標準ユーティリティとしてはコマンドラインシェルがあり、いくつかのプログラミング言語処理系が持つそれに関してはRead-Eval-Print Loopも参照。 ■入力、実行(評価)、出力の繰り返し コマンドラインインタプリタは以下の4つを順に行う。([REPL](https://ja.wikipedia.org/wiki/REPL)) - 入力の読み取り...read - 入力文字列(コマンド)を実行(評価)...evaluate - 実行結果を表示...print - 入力の読み取りに戻る...loop ■キーボード入力と画面出力 一般的に、プログラムは入力と出力を持つ。 Unixの影響を受けたOS(linux,windows,macなど)のコマンドラインプログラムの場合、デフォルト状態ではキーボード(端末)の入力を標準入力、画面(端末)への表示を標準出力として持つ。(エラー時の画面表示は標準エラー出力。) ![](https://i.imgur.com/bozN5do.png) コマンドラインインタプリタも標準入力、標準出力、標準エラー出力を持つ。 ## シェルの実行シーケンス概念図(単純な例) ```plantuml actor OS利用者 as actor participant シェルプロセス as shell participant lsプロセス as ls participant カーネル as kernel activate shell activate kernel actor <- shell : 1.プロンプトを表示してユーザに入力を促す actor -> shell : 2.「 ls」と入力 shell -> shell : 3.標準入力から文字列(コマンド)を読み込む shell -> kernel : 4.入力文字列に該当するプログラムを探して起動する。 kernel -> ls : 5.(シェルの子プロセスとして)\nプログラムを起動 activate ls ls -> shell : 6.実行結果を標準出力に出力する actor <- shell : 7.プログラムの実行結果を表示する ls -> shell : 8.プログラムを終了してそれをシェルに伝える deactivate ls actor <- shell : 1.プロンプトを表示してユーザに入力を促す ``` (キーボード入力と画面表示がどのようにシェルプロセスと入出力を行うかの説明は長くなるので省略) ## goで実装した原始的なシェルプログラムの例 ``` package main import ( "fmt" "bufio" "log" "os" ) func main() { // 標準入力から文字列を読み取る準備 reader := bufio.NewReader(os.Stdin) // 0.ずっとループ for { // 1.プロンプトを表示してユーザに入力を促す fmt.Printf("(ore) > ") // 3.標準入力から文字列(コマンド)を読み込む line, _, err := reader.ReadLine() if err != nil { log.Fatalf("ReadLine %v", err) } // これから起動するプログラムの出力と自分の出力をつなげる(6,7) var procAttr os.ProcAttr procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr} // 4.入力文字列に該当するプログラムを探して起動する process, err := os.StartProcess(string(line), []string{ string(line) }, &procAttr) if err != nil { log.Fatalf("StartProcess %v", err) } // 起動したプログラムが終了するまで待つ(8を待つ) _, err = process.Wait() if err != nil { log.Fatalf("Wait %v", err) } } } ``` [ソースコード](https://github.com/jyami/oreshell/tree/v0.1) ## 実行例(linux ubuntu20.04の場合) ### シェルプログラム起動 ``` $ oreshell (ore) > ``` ### シェルにコマンドを入力 ■「/usr/bin/ls」と入力した場合 ``` $ oreshell (ore) > /usr/bin/ls go.mod main.go (ore) > ``` 期待通り、カレントディレクトリにあるファイル名の一覧を表示した。 ■「/usr/bin/pwd」と入力した場合 ``` $ ./oreshell (ore) > /usr/bin/pwd /home/eijim/go/1.14.2/src/github/masaki-linkode/oreshell (ore) > ``` 期待通り、カレントディレクトリ名を表示した ■「ls」と入力した場合 ``` $ ./oreshell (ore) > ls 2020/11/15 17:12:04 StartProcess fork/exec ls: no such file or directory exit status 1 $ ``` エラーになった。 ■「/usr/bin/ls -al」と入力した場合 ``` $ ./oreshell (ore) > /usr/bin/ls -al 2020/11/15 17:12:48 StartProcess fork/exec /usr/bin/ls -al: no such file or directory exit status 1 $ ``` エラーになった。 ■「cat main.go」と入力した場合 ``` $ ./oreshell (ore) > cat main.go 2020/11/15 17:16:54 StartProcess fork/exec cat main.go: no such file or directory exit status 1 $ ``` エラーになった。 ■「cd ..」と入力した場合 ``` $ ./oreshell (ore) > cd .. 2020/11/15 17:15:28 StartProcess fork/exec cd ..: no such file or directory exit status 1 $ ``` エラーになった。 ■「/usr/bin/ls | /usr/bin/sort > sort.txt &」と入力した場合 ``` $ ./oreshell (ore) > /usr/bin/ls | /usr/bin/sort > sort.txt & 2020/11/15 21:35:20 StartProcess fork/exec /usr/bin/ls | /usr/bin/sort > sort.txt &: no such file or directory ``` なぜエラーになるのか。 その原因の話は[次回以降](https://hackmd.io/@jyami/HyeSkkThP)。