シェルを作成するためには,利用者が打ち込んだ行を,シェルの構文に従って解釈し,何を実行すれば良いのか調べる必要があります. それを構文解析と言います. この実験では構文解析部分をしっかり作ることが目的ではありませんので,構文解析しやすい構文を自分で決めて構いません. 例えば
% wc < data > resultのようなリダイレクション機能を表す < , > の前後には必ずスペースが2つだけあると決めて,その場合だけうまく構文解析できるようなプログラムでも構いません.
秋C学期のプログラミング言語処理では,字句解析,構文解析を勉強しますので,その知識を使ってシェルの構文を構文解析するプログラムを作ってみるのも良いでしょう.
または,解析木を出力するようなプログラムを用意してありますので,シェルの構文解析の部分に,このプログラムを使用しても構いません. 以下は,用意してあるシェル用構文解析器の説明です.
プログラムは,次の場所にあります.
~syspro/shell/
cp -rp などでコピーして使ってください.必要な部分は,次のファイルです.
command.c
command.h
lexer.c
lexer.h
parser.c
parser.h
executer.c
executer.h
mysh.c
parse()
構文解析器で,引数で与えられた1行を解析し,pl_list, pipe_line, single_command という構造体からなる木構造を返します.
parse() が返した木構造を解析して実行(画面に表示)するものが,executer.c に含まれている関数です. このサンプルは,木構造の内容を画面に表示するようになっています.
次のような手順で,executer.c を書き換えていくとよいでしょう.
fork() のタイミングには注意してください. 内部コマンドの場合,fork() をしてはいけません. ただし,標準出力がパイプの場合,まずパイプを作成してからfork() する必要があります. fork() すべきかどうかを,木を先読みして調べる方法もあります.
配列と木構造の探索については,2年生の教科書を読み返して復習して下さい. 配列の場合には,「ループ」することが基本です.パイプラインは,再帰ではなくループを使った方が簡単に実現できるようになっています. 構造体の場合には,関数呼び出し(場合によっては再帰呼び出し)を使います. fork() する時には,2重に検索実行しないように気をつける必要があります.
ループの数は,1つとは限りません. 2つのループを使った方が簡単な場合もあります. 2つのループとは,2重ループではなくて,1つのループを2回やるものです.
for( i=0 ; i < n; i++ ) { } ... for( i=0 ; i < n; i++ ) { }
課題2-1では,プロセスの親子関係を中心に調べなさい. これを調べるには,自分が作成するシェルの参考にするためです. 同じ方法を使ってもよいし,独自の方法を使ってもかまいません.
親子関係を調べるには,ps コマンドを使います. この時,意味はありませんが,ps コマンドには,X ウインドウ関係のプログラム(すぐには終了しない)を使うとよいようです.
% csh本格的に調べるには,strace コマンド (Linux) を使います.% emacs | ps -l
% emacs | kterm | ps -l
% sh
$ emacs | ps -l
$ emacs | kterm | ps -l
$
![]()
% strace -f -o sh.log shプロセスが実行していく過程でどのようにシステムコールを発行していったかの「足跡」をプロセスのトレース(trace)といいます.$ ls
$ ls | head
$ exit
% less sh.log
% egrep 'fork|pie|dup|exec|close|exit|wait' sh.log | less
% egrep 'fork|pie|dup|exec|exit|wait' sh.log | less
%
![]()
詳しくは,man strace を見てください.