シェルを特別視するのはやめよう
おことわり
個人ブログだしたまには思想をぶちまけてもいいかなと思って書き殴っています
また、特定の教材や記事を批判するものではありません
Shell
学生時代から「シェルを特別扱いする」タイプのテキストや言及に辟易していたので、まとめる。
シェルは人間とPC(OS)の架け橋だとか、カーネルを囲んで人間が扱いやすくしたものがシェル。だとか、そういった説明はわかりやすさや字面の良さだけを優先して間違った認識を植え付けるものだと思うので、全て忘れてほしい。
黙って実装すればわかるんだから。
実装
ちょうど学生時代にシステムプログラミングの入門教材として作った実装があったので、これをベースに説明する。
一言で言えばシェルもあくまでREPLである。
極端に簡略化すると、loop
の中で入力を受け取り、fork
してexecvp
に渡しているだけ。
main
ではloop
が呼ばれるloop
は名前通りループしており、その中ではreadln
で標準入力から改行文字までをバッファに読み込むcmd_tokenize
で入力を分割する- 分割された入力を
execute
が受け取り、"exit"
だった場合はsish_exit
を呼び出し、そうでない場合はlaunch
に入力を渡すsish_exit
では0を返し、全体のループを終了させるlaunch
ではfork
を行い、子プロセスでexecvp
を、親プロセスではwait
を行い、終了したら1を返してループに戻る
これを「架け橋」とするならば、「引数で渡されたパスを開き、その内容をread
してstdout
にwrite
する」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などのシェルに機能は遠く及ばないが、根幹となっているのはこのfork
とexec
の部分。
コマンド
また、「Linuxコマンド」という単語に対しても違和感を感じている。
それは自分が「コマンド」に抱いているイメージがズレているせいかもしれないが、一応説明しておく。
例えばcat
やls
だが、これはシェルの機能でもLinuxの機能でもなく、coreutilsと呼ばれるUNIX用のプログラム郡の一つである。
forkして別のプログラムをexecしているのを”コマンド”と呼ぶのに若干の違和感を感じませんか?
強いて言うならシェルの組み込みであることが多いecho
とかwhich
あたりはまぁコマンド感ある。
番外編 PowerShell
PowerShellをご存知だろうか。Windowsユーザなら使ったことがあるだろう、あの.NETのREPLです。
あれは俺がUNIX Shellに抱いているイメージを大幅に破壊する、オブジェクト指向言語のREPL。マジでREPL感がすごい。
まとめ
結局、言いたいのは一つで、なんかよくわからないものをそれっぽい甘い言葉に騙されて理解した気にならずに、実装した方が良いってことです。
coreutils
の再実装も結構いい勉強にもネタにもなるので、昨年の記事で書いたように、シェルやcat、lsを再実装しながら新しい言語の勉強も同時にしてみてはいかがでしょうか。
終わりに
この記事はn01e0 Advent Calendar 2024の18日目の記事とします。
Comments