Try   HackMD

シェルってなに?コマンドラインインタプリタってなに? - シェルもどきをgoで自作する#1

はじめに

「シェルとは何か?コマンドラインインタプリタとは何か?」について「シェルもどき(oreshell)」をgoで作りながら学ぶ。

「・・・ってなに?」

シェルってなに?

wikipediaより

シェル (英語: shell) はオペレーティングシステム(OS)があるような高機能なコンピュータにおいて、ユーザーのためにインタフェースを提供するコンピュータプログラムであり、オペレーティングシステムが提供する機能へのアクセスを提供する。(略)
コンピュータシステムとユーザーの間にある「殻」であることから、このように呼ばれる。
大まかに2種類に大別すると、キャラクタユーザインタフェース(CUI)ベースのコマンドラインインタプリタ(CLI)の形態をとる「コマンドラインシェル」と、グラフィカルユーザインタフェース(GUI)ベースでいわゆる「デスクトップメタファ」等の環境へのユーザインタフェースという形態をとる「グラフィカルシェル」に分けられる。

Unix/Linuxの場合、シェルを説明するときによく使われる図は以下の通り。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

OS利用者はOSの中核(カーネル)に対して、操作/要求を直接送ることはできない。
シェルを通じてカーネルに操作/要求を送る。

  • シェルには以下の2種類がある
    • GUIシェル
    • CUIシェル (今回の話はこちらに限定)
      • unix/linuxのシェルの例
        • bash
        • sh
        • zsh
        • etc
      • unix/linux以外のシェルの例
        • windows の cmd.exe
        • windowsのpowershell
        • etc
      • プログラミング言語のシェル

コマンドラインインタプリタってなに?

wikipediaより

コマンドラインインタプリタ (英: command line interpreter) は、コンピュータのコマンドライン、すなわち、ユーザによりキーボードなどから入力される文字列の行(ライン)のコマンドを解釈し、オペレーティングシステム(以下、OS)やプログラミング言語処理系などに渡す、コマンドラインインタフェースであり、キャラクタユーザインタフェースを構成する要素である。
OSの標準ユーティリティとしてはコマンドラインシェルがあり、いくつかのプログラミング言語処理系が持つそれに関してはRead-Eval-Print Loopも参照。

■入力、実行(評価)、出力の繰り返し

コマンドラインインタプリタは以下の4つを順に行う。(REPL)

  • 入力の読み取りread
  • 入力文字列(コマンド)を実行(評価)evaluate
  • 実行結果を表示print
  • 入力の読み取りに戻るloop

■キーボード入力と画面出力

一般的に、プログラムは入力と出力を持つ。
Unixの影響を受けたOS(linux,windows,macなど)のコマンドラインプログラムの場合、デフォルト状態ではキーボード(端末)の入力を標準入力、画面(端末)への表示を標準出力として持つ。(エラー時の画面表示は標準エラー出力。)

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

コマンドラインインタプリタも標準入力、標準出力、標準エラー出力を持つ。

シェルの実行シーケンス概念図(単純な例)

        
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

(キーボード入力と画面表示がどのようにシェルプロセスと入出力を行うかの説明は長くなるので省略)

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)
    }
  }
}

ソースコード

実行例(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

なぜエラーになるのか。
その原因の話は次回以降