# シェル変数との違いからみた環境変数 - シェルもどきをgoで自作する #14 ## おさらい これまで - [シェルってなに?コマンドラインインタプリタってなに? - シェルもどきを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) - [環境変数の設定と環境変数の展開 - シェルもどきをgoで自作する #13](https://hackmd.io/@jyami/Hy3EefRgj) シェルもどき「[oreshell](https://github.com/jyami/oreshell)」を自作している。 前回は環境変数を実装をした。 (たぶん)次回以降でシェル変数を実装するが、その前に環境変数とシェル変数の違いを確認する。 また、シェル以外のプロセスによる環境変数の扱いを確認する。 ## シェル変数とは > UNIXにおいて、シェルが持つ変数のこと。シェル自体の動作を変更したり、シェルスクリプトで使用したりする。 [コトバンク](https://kotobank.jp/word/%E3%82%B7%E3%82%A7%E3%83%AB%E5%A4%89%E6%95%B0-4088) ## シェル変数と環境変数の使い方は似ている ### シェル変数 設定 ``` $ <変数名>=値 ``` 参照(展開) ``` $<変数名> または ${<変数名>} ``` 一覧表示 ``` $ set ``` 例: ``` $ ABC=abc $ set | grep ABC ABC=abc $ echo ${ABC} abc ``` ### 環境変数 代入 ``` $ export <変数名>=値 ``` 参照(展開) ``` $<変数名> または ${<変数名>} ``` 一覧表示 ``` $ env または $ printenv ``` 例: ``` $ export ABC=abc $ env | grep ABC ABC=abc $ echo ${ABC} abc ``` 環境変数とシェル変数で同名の変数があれば環境変数を優先する([らしい](https://ie.u-ryukyu.ac.jp/~e045713/shell/act2.html))。 ## 環境変数とシェル変数の違い シェル変数は同一シェル内で有効な変数である。 それに対し、環境変数はシェル以外のプロセスも変数を扱うことができる。 また、プロセス自身が持つ変数を、そのプロセスが作成した子プロセスに受け渡すことができる。 ## シェル変数との違いからみた環境変数 環境変数がシェル変数と違う点は以下の2つ。 - シェル以外のプロセスで変数を使うことができる。 - 子プロセスに変数を受け渡すことができる。 Unix(linux,osxも含む)が上記をどのように実現するかは、C言語のプログラムを書いたほうが理解しやすい。 ### シェル以外のプロセスで環境変数を参照 以下は環境変数を参照するC言語のプログラム。 ```helloworld.c #include <stdio.h> #include <stdlib.h> int main(void) { printf("hello env %s\n", getenv("ABC")); getchar(); // プログラムを入力待ちにすることによって一時停止 } ``` 上記、プログラムをコンパイルする。 ``` $ gcc -o helloenv helloenv.c ``` 環境変数を指定してプログラムを実行する。 ``` $ ABC=abc ./helloenv helloenv abc ``` プロセス起動時に指定した環境変数(ABC)をhelloenv内で参照していることがわかる。 ### シェル以外のプロセスが子プロセスを作成し環境変数を受け渡す 以下はhelloenvプロセスを作成するC言語のプログラム。 ```create_helloenv.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { // 環境変数を設定 setenv("ABC", "abc", 1); // プロセスをコピー(環境変数も含む)して分岐 pid_t pid = fork(); if (pid < 0) { // fork失敗 perror("fork error"); exit(-1); } else if (pid == 0) { // 子プロセスで別プログラム(helloenv)を実行 execl("./helloenv", "helloenv", NULL); // 正しく別プログラムを実行できればここに到達しない。 perror("execle error"); exit(-1); } // 親プロセスの処理の続き int status; pid_t r = waitpid(pid, &status, 0); //子プロセス(helloenv)の終了待ち if (r < 0) { perror("waitpid error"); exit(-1); } return 0; } ``` ■ fork forkシステムコール > fork(フォーク)とは、プロセスのコピーを生成するものである。 [wikipedia](https://ja.wikipedia.org/wiki/Fork) ![](https://i.imgur.com/ssVySpR.png) ■ プロセスの分岐を表した図 ![](https://i.imgur.com/DK7Ite9.png) 上記、プログラムをコンパイルする。 ``` $ gcc -o create_helloenv create_helloenv.c ``` プログラムを実行する。 ``` $ ./create_helloenv helloenv abc ``` create_helloenvが起動後に内部で設定した環境変数(ABC)を、子プロセスであるhelloenvに受け渡していることがわかる。 最近知ったのだが、Linuxは、「/proc/<プロセスID>/environ」でプロセス起動時の環境変数を確認することができる。 以下は「/proc/<プロセスID>/environ」でhelloenv起動時のの環境変数を確認している様子。 ``` $ ps aux | grep helloenv eijim 12645 0.0 0.0 2492 512 pts/8 S+ 00:37 0:00 ./create_helloenv eijim 12646 0.0 0.0 2492 576 pts/8 S+ 00:37 0:00 helloenv eijim 12655 0.0 0.0 7484 656 pts/5 S+ 00:42 0:00 grep --color=auto helloenv $ cat /proc/12646/environ | sed -e 's/\x0/\n/g' | grep ABC ABC=abc ```