できない.dev

環境変数を削除するには

プログラムの中で設定済みの環境変数を削除する基本形を各言語で示す。
空文字を入れても削除にはならない点と、自プロセスの環境を書き換えられない言語での代わりの方法までを扱う。

公開:

選んだ言語は、ほかのサンプルコードを開いたときも最初に表示する。

Python

import os
 
os.environ["API_TOKEN"] = "abc123"
del os.environ["API_TOKEN"]                # 削除する。未設定だと KeyError
print("API_TOKEN" in os.environ)           # False
 
os.environ["API_TOKEN"] = "abc123"
print(os.environ.pop("API_TOKEN", None))   # abc123(未設定でも例外にならない)
 
os.environ["EMPTY"] = ""                   # 空文字を入れても削除にはならない
print("EMPTY" in os.environ)               # True

del os.environ["KEY"] は未設定のキーに対して KeyError を投げるので、あるかどうか分からないときは os.environ.pop("KEY", None) を使う。
os.environ の削除は内部で unsetenv を呼ぶため、以降に起動する子プロセスにも反映される。
空文字を代入しても変数は残り、in は True のままである。

できないときPython「KeyError」が dict のアクセスで出る原因と直し方

JavaScript

process.env.API_TOKEN = "abc123";
delete process.env.API_TOKEN;                      // 削除する
console.log("API_TOKEN" in process.env);           // false
 
process.env.API_TOKEN = undefined;                 // 削除にはならない
console.log(process.env.API_TOKEN === "undefined"); // true(文字列 "undefined" が入る)
console.log("API_TOKEN" in process.env);           // true

delete process.env.KEY で変数が消え、in 演算子でも存在しなくなる。
process.env.KEY = undefined は削除ではなく、値が文字列の "undefined" に変換されて変数が残る。
消すつもりで undefined を入れると、後続の「設定されているか」の判定が常に真になる。

TypeScript

process.env.API_TOKEN = "abc123";
 
// プロパティは string | undefined 型なので delete で削除できる(型エラーにならない)
delete process.env.API_TOKEN;
console.log(process.env.API_TOKEN);            // undefined
 
// undefined を代入しても削除されず、文字列 "undefined" になる
process.env.API_TOKEN = undefined as unknown as string;
console.log(process.env.API_TOKEN === "undefined"); // true

process.env の値は string | undefined 型の省略可能なプロパティなので、delete を厳密モードでも型エラーなしで使える。
undefined の代入は型で弾かれるが、キャストで通すと JavaScript と同じく文字列の "undefined" になる。

Go

package main
 
import (
	"fmt"
	"os"
)
 
func main() {
	os.Setenv("API_TOKEN", "abc123")
	if err := os.Unsetenv("API_TOKEN"); err != nil { // 削除する
		fmt.Println(err)
		return
	}
	_, ok := os.LookupEnv("API_TOKEN")
	fmt.Println(ok) // false
 
	os.Setenv("API_TOKEN", "") // 空文字を入れても削除にはならない
	_, ok = os.LookupEnv("API_TOKEN")
	fmt.Println(ok) // true
}

os.Unsetenv で変数を削除する。
os.Setenv("KEY", "") は値を空にするだけで変数は残るため、os.LookupEnv の戻り値 ok は true のままである。
存在の有無を判定したいときは os.Getenv ではなく os.LookupEnv を使う。

Rust

use std::env;
use std::process::Command;
 
fn main() {
    // 子プロセスの環境から外すだけなら安全な API で済む
    let status = Command::new("printenv")
        .arg("HOME")
        .env_remove("HOME")
        .status()
        .expect("failed to run printenv");
    println!("{:?}", status.code()); // Some(1)(HOME が無いので printenv は 1 で終わる)
 
    // 自プロセスの環境変数を消す(edition 2024 では unsafe)
    unsafe { env::set_var("API_TOKEN", "abc123") };
    unsafe { env::remove_var("API_TOKEN") };
    println!("{}", env::var("API_TOKEN").is_err()); // true
 
    unsafe { env::set_var("API_TOKEN", "") }; // 空文字を入れても削除にはならない
    println!("{:?}", env::var("API_TOKEN")); // Ok("")
}

edition 2024 では std::env::remove_var が unsafe になっており、unsafe ブロックなしで呼ぶと E0133 でビルドが止まる。
他のスレッドが環境を読んでいる最中に書き換えるとデータ競合になりうるためである。
子プロセスから外すだけなら Command::env_remove が安全な方法で、自プロセスの環境は触らずに済む。

Java

public class Main {
    public static void main(String[] args) throws Exception {
        // System.getenv() は読み取り専用なので remove できない
        try {
            System.getenv().remove("HOME");
        } catch (UnsupportedOperationException e) {
            System.out.println("System.getenv() は書き換えられない");
        }
 
        // 子プロセスの環境からは ProcessBuilder のコピーで外せる
        ProcessBuilder pb = new ProcessBuilder("printenv", "HOME");
        pb.environment().remove("HOME");
        pb.inheritIO();
        System.out.println(pb.start().waitFor()); // 1(HOME が無いので printenv は 1 で終わる)
    }
}

Java には自プロセスの環境変数を削除する標準 API がなく、System.getenv() が返すマップは変更不可で remove すると UnsupportedOperationException になる。
子プロセスから外したいときは ProcessBuilder.environment() が返す環境のコピーに対して remove を呼ぶ。
この変更は start() で起動するその子プロセスにだけ反映される。

C#

using System;
 
Environment.SetEnvironmentVariable("API_TOKEN", "abc123");
Environment.SetEnvironmentVariable("API_TOKEN", null);   // null を渡すと削除される
Console.WriteLine(Environment.GetEnvironmentVariable("API_TOKEN") is null); // True
 
Environment.SetEnvironmentVariable("API_TOKEN", "");     // .NET 9 以降は空文字のまま残る
Console.WriteLine(Environment.GetEnvironmentVariable("API_TOKEN") is null); // False(.NET 8 までは True)

Environment.SetEnvironmentVariable に null を渡すと変数が削除される。
空文字の扱いは版で違い、.NET 9 以降は空文字のまま変数が残るが、.NET 8 までは削除になる。
どの版でも消したいときは null を渡す。

空文字を入れても削除にはならない

環境変数を「なくす」ことと「空にする」ことは別である。
Python の os.environ["KEY"] = ""、Go の os.Setenv("KEY", "")、Rust の env::set_var("KEY", "") はいずれも変数を残したまま値だけを空にするので、存在を調べる in・os.LookupEnv・env::var は設定済みとして答える。
C# の Environment.SetEnvironmentVariable も .NET 9 以降は同じだが、.NET 8 までは空文字を渡すと削除になる。
言語をまたいで同じ意味にしたいときは、削除用の API を明示的に呼ぶのが確実である。

JavaScript で undefined を代入しない

process.env.KEY = undefined は削除に見えるが、実際には文字列の "undefined" が入る。
環境変数の値は常に文字列で、代入時に文字列へ変換されるためである。
消したいときは delete process.env.KEY を使う。
値の有無を if (process.env.KEY) で判定している場所では、"undefined" という文字列が真と扱われて気づきにくい。

Java は自プロセスの環境を書き換えられない

Java には実行中のプロセスの環境変数を変える標準の方法がない。
起動後に設定や削除をしたい値は、ProcessBuilder.environment() で子プロセスにだけ渡す形にするか、起動時に -D でシステムプロパティとして渡す設計にする。
Rust も edition 2024 では自プロセスの環境変数の書き換えが unsafe になり、同じ方向(子プロセス側で制御する)が勧められている。