おことわり

個人ブログだしたまには思想をぶちまけてもいいかなと思って書き殴っています

また、特定の教材や記事を批判するものではありません

Shell

学生時代から「シェルを特別扱いする」タイプのテキストや言及に辟易していたので、まとめる。

シェルは人間とPC(OS)の架け橋だとか、カーネルを囲んで人間が扱いやすくしたものがシェル。だとか、そういった説明はわかりやすさや字面の良さだけを優先して間違った認識を植え付けるものだと思うので、全て忘れてほしい。

黙って実装すればわかるんだから。

実装

ちょうど学生時代にシステムプログラミングの入門教材として作った実装があったので、これをベースに説明する。

一言で言えばシェルもあくまでREPLである。

sish

極端に簡略化すると、loopの中で入力を受け取り、forkしてexecvpに渡しているだけ。

  1. mainではloopが呼ばれる
  2. loopは名前通りループしており、その中では
    1. readlnで標準入力から改行文字までをバッファに読み込む
    2. cmd_tokenizeで入力を分割する
    3. 分割された入力をexecuteが受け取り、"exit"だった場合はsish_exitを呼び出し、そうでない場合はlaunchに入力を渡す
      • sish_exitでは0を返し、全体のループを終了させる
      • launchではforkを行い、子プロセスでexecvpを、親プロセスではwaitを行い、終了したら1を返してループに戻る

これを「架け橋」とするならば、「引数で渡されたパスを開き、その内容をreadしてstdoutwriteする」catも架け橋なんじゃないですかね。

非常にプリミティブなものではあるが、Cでも200行程度で実装できる。

Pythonで書いたら20行

import os

while True:
    command = input("> ")
    if command.strip() == "exit":
        print("Exiting shell...")
        break

    pid = os.fork()

    if pid == 0:
        try:
            args = command.split()
            os.execvp(args[0], args)
        except Exception as e:
            print(f"An error occurred: {e}")
        os._exit(1)
    elif pid > 0:
        os.wait()
    else:
        print("Failed to fork process.")

bashやzshなどのシェルに機能は遠く及ばないが、根幹となっているのはこのforkexecの部分。

コマンド

また、「Linuxコマンド」という単語に対しても違和感を感じている。

それは自分が「コマンド」に抱いているイメージがズレているせいかもしれないが、一応説明しておく。

例えばcatlsだが、これはシェルの機能でもLinuxの機能でもなく、coreutilsと呼ばれるUNIX用のプログラム郡の一つである。

forkして別のプログラムをexecしているのを”コマンド”と呼ぶのに若干の違和感を感じませんか?

強いて言うならシェルの組み込みであることが多いechoとかwhichあたりはまぁコマンド感ある。

番外編 PowerShell

PowerShellをご存知だろうか。Windowsユーザなら使ったことがあるだろう、あの.NETのREPLです。

あれは俺がUNIX Shellに抱いているイメージを大幅に破壊する、オブジェクト指向言語のREPL。マジでREPL感がすごい。

まとめ

結局、言いたいのは一つで、なんかよくわからないものをそれっぽい甘い言葉に騙されて理解した気にならずに、実装した方が良いってことです。

coreutilsの再実装も結構いい勉強にもネタにもなるので、昨年の記事で書いたように、シェルやcat、lsを再実装しながら新しい言語の勉強も同時にしてみてはいかがでしょうか。

終わりに

この記事はn01e0 Advent Calendar 2024の18日目の記事とします。