スタックとヒープの違いが見抜ける人でないと(putenv()
を使うのは)難しい
『 Linuxプログラミングインタフェース』6章の環境変数の続き。
C言語で環境変数を扱うための関数はstdlib.h
に用意されているが、その中でも扱いが難しいのがputenv()
である。
int putenv(char *string);
という形式で、char *string
の値がコピーされるのではなく、グローバル変数environ
の中の要素が*string
の値を指すという仕様である。
つまり、スタック上にある文字列を渡してしまうと関数が終わった後に別の値に上書きされてしまう。
C言語の場合、スタックとヒープの違いをよくわかっていないとよくある関数すら使いこなせないのである。
厳しい世界である。
なお、CERT セキュアコーディングスタンダードでも注意喚起がされている。
サンプルプログラムmodify_env.c
の仕様は以下の通り。サンプルの意図としては、6.7節で説明したgetenv()
, putenv()
, setenv()
, unsetenv()
, clearenv()
を全て使う例である。
- 初めに環境変数をすべて削除する
- コマンドラインパラメータで与えられたものを環境変数に加える
GREET
という環境変数が存在しなければ加えるBYE
という環境変数があれば削除する- 最後にすべての環境変数を表示する
Python
os.environ
が辞書なので辞書に対する操作をそのまま適用すれば良い。os.putenv()
などは使わない。
import argparse import os def modify_env(environs): os.environ.clear() for arg in environs: key, value = arg.split("=") os.environ[key] = value if "GREET" not in os.environ: os.environ["GREET"] = "Hello world" if "BYE" in os.environ: del os.environ["BYE"] for key, value in os.environ.items(): print(f"{key}={value}") def main(): parser = argparse.ArgumentParser() parser.add_argument("environs", nargs="*") args = parser.parse_args() modify_env(args.environs) if __name__ == "__main__": main()
実行結果は以下の通りである。
(venv) $> python3.7 modify_env.py GREET="Guten Tag" SHELL=/bin/bash BYE=Ciao GREET=Guten Tag SHELL=/bin/bash (venv) $> python3.7 modify_env.py SHELL=/bin/bash BYE=byebye SHELL=/bin/bash GREET=Hello world
Rust
std::env
にある関数を活用すれば良い。- 環境変数の有無のチェックは
std::env::var()
で行う。Result
型なのでうまく処理する必要がある。 - コマンドライン引数の解析がちょっとぎこちない。
use std::env; fn modify_env(args: &Vec<String>) { for (key, _) in env::vars() { env::remove_var(key); } for arg in args { let splited_arg: Vec<_> = arg.split('=').collect(); let (key, value) = (&splited_arg[0], &splited_arg[1]); env::set_var(key, value); } if env::var("GREET").is_err() { env::set_var("GREET", "Hello world"); } if env::var("BYE").is_ok() { env::remove_var("BYE"); } for (key, value) in env::vars() { println!("{}={}", key, value); } } fn main() { let mut _args = Vec::new(); for arg in env::args().skip(1) { _args.push(arg); } modify_env(&_args); }
実行結果は以下の通りである。
$> target/debug/modify_env GREET="Guten Tag" SHELL=/bin/bash BYE=Ciao GREET=Guten Tag SHELL=/bin/bash $> target/debug/modify_env SHELL=/bin/bash BYE=byebye SHELL=/bin/bash GREET=Hello world
感想
- C言語の
putenv()
の扱いが難しい。 - 他の言語ではそれなりに扱いやすくなっているが、良からぬことが起きたりしないだろうかと心配してしまう。