何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

『 Linuxプログラミングインタフェース』を気合いで読む会 第7回:6章プロセス:環境変数の編集

スタックとヒープの違いが見抜ける人でないと(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()を全て使う例である。

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()の扱いが難しい。
  • 他の言語ではそれなりに扱いやすくなっているが、良からぬことが起きたりしないだろうかと心配してしまう。