# 環境変数の設定と環境変数の展開 - シェルもどきをgoで自作する #13 ## おさらい これまで - [シェルってなに?コマンドラインインタプリタってなに? - シェルもどきをgoで自作する#1](https://hackmd.io/@jyami/HJzohRn2D) - [コマンドと引数の分解、環境変数PATHから探索、外部コマンドと内部コマンド - シェルもどきをgoで自作する #2](https://hackmd.io/@jyami/HyeSkkThP) - [字句解析その1 - シェルもどきをgoで自作する #3](https://hackmd.io/@jyami/Hk3bWSMQO) - [字句解析その2 - シェルもどきをgoで自作する #4](https://hackmd.io/@jyami/S1BkltxQu) - [リダイレクションってなに? - シェルもどきをgoで自作する #5](https://hackmd.io/@jyami/S113NQx8u) - [リダイレクションの種類 - シェルもどきをgoで自作する #6](https://hackmd.io/@jyami/rJ3XmClqd) - [リダイレクションの構文 - シェルもどきをgoで自作する #7](https://hackmd.io/@jyami/BJ04J2Upd) - [コマンドプロセスを作成する際のファイルディスクリプタの操作 - シェルもどきをgoで自作する #8](https://hackmd.io/@jyami/Hy7nSciMt) - [構文解析と抽象構文木 - シェルもどきをgoで自作する #9](https://hackmd.io/@jyami/ByrK1ajIK) - [パイプってなに? - シェルもどきをgoで自作する #10](https://hackmd.io/@jyami/SkXR3iltK) - [ワイルドカードってなに? - シェルもどきをgoで自作する #11](https://hackmd.io/@jyami/SJghKSl3F) - [環境変数ってなに? - シェルもどきをgoで自作する #12](https://hackmd.io/@jyami/H12wYg4L9) シェルもどき「oreshell」を自作している。 前回は環境変数の解説をした。 今回は環境変数をoreshellに実装する。 ## 今回対応する内容 ### 環境変数の名前 POSIXでの仕様は[こちら](https://blog.yukii.work/posts/2021-12-03-posix-specification-of-environment-variables/#gsc.tab=0)によると - 英字(大文字と小文字)と数字とアンダーバー - 先頭は数字以外 らしいのでこれに準ずる。 ### 環境変数の設定 今回対応するのは以下のみ。 ``` $ export HOGE=hige # 現在のシェルの環境変数に値を代入 ``` 以下は対応しない。 ``` $ HOGE=hige # シェル変数に値を代入 $ export HOGE # シェル変数を環境変数に登録 ``` ``` $ PGPASSWORD=パスワード psql ... # 発行するプロセスの環境変数に値を渡す ``` その他。 ### 環境変数の展開 今回対応したするのは以下のみ。 ``` $ echo $HOGE ``` ``` $ echo ${HOGE} ``` 概要図 ## 設計![](https://i.imgur.com/MCqh9F2.png) ### 環境変数の設定 内部コマンドexportを新設する。 exportコマンドは、OSカーネルに対して環境変数名とその値の設定を行う。 ### 環境変数の展開 環境変数の展開により、 - コマンド文から環境変数名を拾い出す。 - OSカーネルに該当する環境変数の値を取得をする - OSカーネルから取得した値で、コマンド文内の環境変数名箇所を置き換える。 を行う。 #### 環境変数の展開をインタプリタの処理の流れのどこで行うか 環境変数の展開をインタプリタの処理の流れのどこで行うかについて検討したが、[以前に対応した ワイルドカードによるファイル名展開](https://hackmd.io/@jyami/SJghKSl3F#bash%E3%81%A7%E3%81%AF%E3%80%8CFilename-Expansion%E3%80%8D%E3%81%A8%E5%91%BC%E3%81%B6)では、構文解析器が抽象構文木を出力する際に行っていた。 ワイルドカードによるファイル名展開は[bashのshell expansions](https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html)(いい日本語訳が見つからない)を参考にしている。 環境変数の展開は、ワイルドカードによるファイル名展開と同じようにshell expansionsのひとつである。 よって、環境変数の展開もファイル名展開と同じく構文解析器が抽象構文木を出力する際に行うよう修正する。 なお、oreshellのshell expansionsの順番は以下の通り。 - 環境変数展開 ←今回追加 - ワイルドカードによるファイル名展開 - クオート文字、エスケープ文字の削除  「クオート文字、エスケープ文字の削除」については[こちら](https://hackmd.io/@jyami/SJghKSl3F#%E3%81%A4%E3%81%BE%E3%81%A5%E3%81%84%E3%81%9F%E3%81%A8%E3%81%93%E3%82%8D%E3%80%81%E3%83%8F%E3%83%9E%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%93%E3%82%8D)を参照。 ## 実装 [こちら](https://github.com/masaki-linkode/oreshell/commit/b84b68b891f1f5783e6c15ecd16cf55f260556d9)を参照。 ## 実行例 ``` (ore) > export HOGE=hige (ore) > env | grep HOGE # 環境変数に登録していることを確認 HOGE=hige (ore) > echo $HOGE hige (ore) > echo ${HOGE} hige (ore) > echo "私の名前は${HOGE}です" 私の名前はhigeです (ore) > ./oreshell     # 子シェルを起動 (ore) > echo $HOGE # 親シェルで登録した環境変数が子シェルにも存在する hige ``` エッジケース正常系 ``` (ore) > export _HUGE_=uge # 環境変数名の先頭文字は英字またはアンダーバー (ore) > echo $_HUGE_ uge ``` ``` (ore) > export HOGE=hige (ore) > echo ">$HOGE<" # 環境変数名の終端は英数字とアンダーバー以外のところ >hige< ``` ``` (ore) > export ABC=abc (ore) > echo $ABC abc (ore) > env | grep ABCDEF # 環境変数ABCDEFは存在しない (ore) > echo $ABCDEF # 環境変数ABCと文字列DEをならべると # 環境変数ABCDEFが見つからないため空文字を出力 (ore) > echo ${ABC}DEF # {}で区切りを明示する abcDEF ``` エッジケース異常系 ``` (ore) > export HOGE-=hige # 環境変数名が英数字とアンダーバー以外を含むとエラー export: HOGE-=hige : not a valid identifier ``` ``` (ore) > export 0HOGE=hige # 環境変数名の先頭文字が数字だとエラー export: 0HOGE=hige : not a valid identifier ``` ## おまけ:今回実装してみて気づいたこと ``` (ore) > ls * a.txt b.txt (ore) > export HOGE=* (ore) > ls ${HOGE} ``` をすると何が起きるか? 答えはご想像通り ``` a.txt b.txt ``` である。 これは、shell expansionsの順番が 1. 環境変数展開 2. ワイルドカードによるファイル名展開 となっているためこうなる。 上記の展開によって引数を示すワード「${HOGE}」は以下の通り変化する。 1. 環境変数展開 - 展開前のワード : ```${HOGE}``` - 展開後のワード : ```*``` 2. ワイルドカードによるファイル名展開 - 展開前のワード : ```*``` - 展開後のワード : ```a.txt b.txt``` ■ 順序が逆だと? この順序が逆、つまり - 1.ワイルドカードによるファイル名展開 - 2.環境変数展開 だと、上記の展開によって引数を示すワード「${HOGE}」は以下の通り変化する。 1. ワイルドカードによるファイル名展開 - 展開前のワード : ```${HOGE}``` - 展開後のワード : ```${HOGE}``` 2. 環境変数展開 - 展開前のワード : ```${HOGE}``` - 展開後のワード : ```*``` 結果、 ``` (ore) > export HOGE=* (ore) > ls ${HOGE} ls: '*' にアクセスできません: そのようなファイルやディレクトリはありません ``` となる。 [bashのshell expansions](https://www.gnu.org/software/bash/manual/html_node/Shell-Expansions.html)の順序の記述にはちゃんとした意味があることが良くわかった。