rust-shell - 背景
歴史的経緯
シェルの発展
- 最初のUnixシェル
- パイプの導入 - シェルスクリプトの基礎
- 変数、制御構文 - C言語風の構文
- ヒストリー機能 - Bourne Shell 互換
- 現在の標準パイプの革命
# Ken Thompson が Doug McIlroy のアイデアを実装
# "一つのことをうまくやる" Unix 哲学の体現
cat file | grep pattern | sort | uniq
コンピュータサイエンス的な意味
プロセス生成
fork() システムコール:
1. 親プロセスを複製
2. 子プロセスを生成
3. 両方が fork() から戻る
- 親: 子のPIDを返す
- 子: 0を返す
exec() システムコール:
- 現在のプロセスを別のプログラムに置換
- メモリ空間を上書き
ファイルディスクリプタ
| FD |
名前 |
説明 |
| 0 |
stdin |
標準入力 |
| 1 |
stdout |
標準出力 |
| 2 |
stderr |
標準エラー |
リダイレクションの仕組み
// stdout をファイルにリダイレクト
let file = File::create("output.txt")?;
dup2(file.as_raw_fd(), 1); // FD 1 (stdout) を置換
// パイプ
let (read_fd, write_fd) = pipe()?;
// 子プロセス1: stdout → write_fd
// 子プロセス2: read_fd → stdin
実践での活用
シェルスクリプトの例
#!/bin/bash
# ログファイルの解析
cat access.log |
grep "ERROR" |
awk '{print $4}' |
sort |
uniq -c |
sort -rn |
head -10
プロセス間通信
┌──────────┐ pipe ┌──────────┐
│ Process1 │ ──────────→ │ Process2 │
│ stdout │ │ stdin │
└──────────┘ └──────────┘
実世界とのギャップ
Rustでのシステムコール
use std::process::{Command, Stdio};
// Command API は fork/exec を抽象化
let child = Command::new("ls")
.arg("-la")
.stdout(Stdio::piped())
.spawn()?;
// 低レベル操作には nix クレート
use nix::unistd::{fork, ForkResult};
match unsafe { fork() } {
Ok(ForkResult::Parent { child }) => { /* 親 */ }
Ok(ForkResult::Child) => { /* 子 */ }
Err(_) => { /* エラー */ }
}
シェルの複雑さ
# 本物のシェルが処理すべきもの:
echo "hello world" # クォート処理
echo $HOME # 変数展開
echo $(date) # コマンド置換
ls *.txt # グロブ展開
cmd1 && cmd2 || cmd3 # 論理演算子
if [ -f file ]; then ...; fi # 制御構文