minishell
2021/2/15
結論
shellの中では環境変数を直接(extern char **environ)見に行かないようにしておく
shellの中では、shell変数を持っておく(shell変数にはkeyとvalueとexportedを持っておく)
shell起動時に環境変数でshell変数を初期化する
以降は、環境変数を扱いたいときもshell変数を参照するようにしておく
気になったので、unset HOMEしてから、HOME=/Users/hogeとやったらどうなるか見てみた
普通のexportされたshell変数であれば、unsetすれば、exportedじゃなくなるのでexportしてもHOMEは出てこないはず
zsh -> exportedとして復活する
bash -> exportで出てこない
shellから子プロセスをforkしてexecする際
shell変数をなめてexportedされていてかつ値が存在するものから、envpを作って、execveする。
export周りを見ていた
exportはshell変数を環境変数化する事ができるコマンド
1) export FOOとしたケース
1-1) 他のプロセスを起動した(例えばzsh)
exportコマンド実施
何もなし(FOOは環境変数にセットされていない)
1-2) FOO=bとする
環境変数にセットされる
1-2-1) 他のプロセスを起動した
FOO=b
1-2-2) 現在のプロセス
exportコマンドを実施
FOO=b
1-3) 現在のプロセス
exportコマンドを打つと
FOO=''と表示
cの中でenvironの中を見る
FOOはない
つまりこの場合、環境変数にセットされてない
2) export FOO=aとしたケース
環境変数にセットされる
2-1) 他のプロセスを起動した
FOO=a
2-2) 現在のプロセス
FOO=a
3) (shell変数として定義済みのHOGE=123があるときに)export HOGEする
環境変数にセットされる
3-1) 他のプロセスを起動した
HOGE=123
3-2) 現在のプロセス
HOGE=123
つまりまとめると
子プロセスにおいて
shell変数がexportされていて、かつshell変数の中身が定義されているときのみ環境変数として子プロセスに引き継がれる
現在のプロセスにおいて
shell変数がexportされていれば、exportで未定義値として出力はされる
この挙動を実現するためには
shell起動時に、環境変数をもとにシェル変数を初期化する(linked listか何かで持っておく)
shellではexportedフラグをもたせてshell変数を定義しておく。
export時は、shell変数をなめて、exportedなものをリストで表示
子プロセスを起動する際は、shell変数をなめて、exportedかつ値があるもののみenvpとして配列にして子プロセスにわたすようにすればうまくいく
2021/2/17
assignmentを作っていく
HOGE=fuga ./a.outとすると、子プロセスの実行環境の環境変数にセットすることになる
前提
1) コマンドの前のアサインメントは、現在のshellプロセス内のshell変数を書き換えることはない(HOME=/ echo; echo $HOMEでHOMEが書き換わっていないこと及び、
2) 子プロセスの環境の環境変数にセットして実行される (HOME=/ envではHOMEは/に書き換わっていることから)
3) cd/exportもいずれも現shell環境で実行され、外部プロセスで実行されているわけではない(man builtinから)
cdに関して
HOME=/ echo $HOMEとHOME=/ cdとHOME=/ exportの挙動の違いについて
まずman builtinでみると
HOME=/ echo $HOMEは
アサインよりも先に$HOMEが置き換わっているから
プロセスに渡るときにすでに、$HOMEが現在のシェル環境のシェル変数に置き換わっている
HOME=/ cd は
環境変数の$HOMEを読み取って移動先を決めてるはず
HOME=/ exportは
環境変数一覧を読み取って、値を表示しているはず
なので、両者の$HOMEは同じになるはずなので、cdの移動先が / ならばexportのHOMEの値も /になってほしい
が、ならない。
ちなみにこのコマンドのあとのecho $HOMEはもとの値のママである
説1) 一時的に現環境のシェル変数を書き換えて、もとに戻してる説
この場合、cdは現在のshell変数を読みにいっていて、exportは、環境変数を読みに行っていると考えれば辻褄があう
説2) cdが外部プロセスで動いている説
その場合、別プロセスであれば、現環境にcdが影響を与えられないので、シグナルで受け取るようにする必要がある
結局Bashの実装を見ると
Assignment variableは一時環境変数を作って、コマンドにはそれらをマージして環境変数としてセットして渡して、builtinコマンドの場合、cdはHOMEを取るときに、一時環境変数を見てから、Shell変数を見る。exportは一時環境変数をみないからこのような挙動になっている模様
新たな疑問としては、PWDの環境変数って何に使ってるんだろう
2021/2/22
exportとかunsetでチェックする必要がありそげ
code:legal_identifier
/* Return 1 if this token is a legal shell `identifier'; that is, it consists
solely of letters, digits, and underscores, and does not begin with a
digit. */
int
legal_identifier (name)
char *name;
{
register char *s;
unsigned char c;
if (!name || !(c = *name) || (legal_variable_starter (c) == 0))
return (0);
for (s = name + 1; (c = *s) != 0; s++)
{
if (legal_variable_char (c) == 0)
return (0);
}
return (1);
}
legal_variable_starter(c) (ISALPHA(c) || (c == '_'))
legal_variable_char(c) (ISALNUM(c) || c == '_')
2021/3/1
変数の出力をできるようにする
まずその前に、ダブルクオーテーションの対応を行っていった
最初に実装したのは、トークンとして、"を認識して、TK_WORDではなくて、TK_DOUBLE_QUOTEのようにして、ダブルクオーテーションで囲まれた単語(スペース含む)を認識して、TK_WORDに更にスプリットするという実装
コレだとだめで、ech"o"みたいなやつはechoとして認識してほしいのが、
ech -> oという風に分かれてしまうのでだめだった。
第二案、シンプルにTK_WORDの中で、"が来たら、次の"が来るまで"も含めて単語にしてしまう。
その後で、"を除外するようにしていく
"を除外する時は、charをmemmoveを使って一文字ずつずらすようにするとうまく行った。
tilda exapantions
parameter expansions
command substitutions
arithmeric expansions
quote removals
2021/3/10
double quote/single quoteをtokenに対してやるのではなくて、nodeに対してやるように変更した。
現状タイミング的には、parseのあとにしている。が、本当は、コマンド実行直前にcompound-listの一つ一つに対して実行すべきであろう。
shell parameter expandの実装
タイミングは、コマンド実行直前に行う。
文字列のreplaceコマンドは、char *replace_char(char *orig, size_t start, size_t end, char *replacer, t_err *err)みたいな形にして行うようにした。
ソースを読んだ感じ、parseまでやってしまって、コマンドを実行するときに一つ一つのコマンドグループに対して、expansionして、クオート除去して、コマンド実行して、っていうのを繰り返す感じなのが良さそう。
2021/4/19
execveは、エラーが出ない限りは中でexitするみたい
code: Return value
As the execve() function overlays the current process image with a new process image, the
successful call has no process to return to. If execve() does return to the calling process, an
error has occurred; the return value will be -1 and file_error (command);
the global variable errno is set to indicate the error.
2021/4/21
shellの中身をみて、バイナリじゃなくて、テキストだったら、subshellで実行できるようにする必要があるが、面倒なので一回おく
PWD/OLDPWDの挙動 (variables.c)
HOMEを取得する (home_string)
現時点でセットされているPWDを取得する (tmp_var)
1) tmp_varがあって、environmentから取り込まれている、tmp_varに入ってる文字列と".'"と同じパスなら、ワーキングディレクトリをtmp_varに入ってる文字列にする
2) home_stringがあって、interactive_shellで、login_shellで、home_stringと"."が同じパスなら、
カレントワーキングディレクトリをhome_stringにする
PWDをhome_stringにする
PWDをexportedにする
3) 1), 2)でもない時
temp_stringをshell_initからワーキングディレクトリを取得してセットする
temp_stringが取得できれば、PWDをtemp_stringにする
PWDをexportする
OLDPWDは、NULLにリセットしておく。
2021/4/26
single quotationのバグがあったので修正
tokenize時に毎回フラグをリセットしないといけないのをしてなかった。
OLDPWDは先に更新してしまっていい
というわけではない。
cd ''はfailしてない
2021/4/28
エラー出力の文字列を何とかする
引数を4つとるようにした(強引ではある・・)
HOMEがないときは、cd: HOME not setとエラーを出すようにした。
実行権限しかないdirectoryにchdirしたときのcurrent_pathの読み取りでSEGVしてたのを修正
ファイル名に関しては、ケースインセンシティブなので、Cdとかは、Pathで/usr/bin/cdがあれば、みつかるので、command not foundにはならないのを修正
path/pathのテストケース全部通った
Permision Deniedは絶対パスをエラーメッセージに入れる
exitのステータスがexecveの返り値を見ていたが、errnoを見るべきだった
OLDPWDはvalueを""にしていたが、NULLじゃないといけなかった
2021/5/10
preprocess/interpolationやっていく
"|$TEST|"みたいなときに、変数名が$TEST|として認識されていたのを修正
上と同じで、区切り文字に$が入ってなかったので
echo $A$BとかしたときにA$Bという変数めいになっていたのを修正
上記と同じ"$A,$B"
途中に'が挟まってるパターンでうまく保管できていなかった echo $A'$B'のようなやつ。
修正した。
2021/5/17
2021/6/21
シェル変数に空白が連続で入るような場合
""でくくられていた場合は、そのまま出力し、そうでない場合は、空白が2つ以上繋がる場合は、空白を一つにする
builtin
まで。
2021/6/28
exportのescapeを対応
具体的には、exportで出力するときに", \`$`
$ だったら前に \を出力する