環境変数を真偽値として読むには
環境変数に入った "true" や "false" などの文字列を真偽値として読み取る基本形を各言語で示す。
文字列の "false" が真と評価される落とし穴と、標準関数が受け付ける表記の言語ごとの違い、未設定や不正な値の扱いまでを扱う。
公開:
各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。
Python 実行確認済み
import os
TRUE_VALUES = {"1", "true", "yes", "on"}
FALSE_VALUES = {"0", "false", "no", "off"}
def env_bool(name: str, default: bool = False) -> bool:
raw = os.getenv(name)
if raw is None or raw.strip() == "":
return default # 未設定・空文字は既定値
value = raw.strip().lower()
if value in TRUE_VALUES:
return True
if value in FALSE_VALUES:
return False
raise ValueError(f"{name} は真偽値として解釈できない: {raw!r}")
# DEBUG=false で実行した場合
print(bool(os.getenv("DEBUG"))) # -> True(空でない文字列はすべて真)
print(env_bool("DEBUG")) # -> False
print(env_bool("VERBOSE", True)) # 未設定なら -> TruePython には環境変数の文字列を真偽値にする標準関数が無く、bool() で変換すると "false" や "0" も空でない文字列なので True になる。
以前使われた distutils.util.strtobool は Python 3.12 で distutils ごと削除され、import すると ModuleNotFoundError になるため、受け付ける値の集合を自前で定義して比較する。
うまくいかない時: Python import できない(ModuleNotFoundError)
JavaScript 実行確認済み
const TRUE_VALUES = new Set(["1", "true", "yes", "on"]);
const FALSE_VALUES = new Set(["0", "false", "no", "off"]);
function envBool(name, defaultValue = false) {
const raw = process.env[name];
if (raw === undefined || raw.trim() === "") return defaultValue; // 未設定・空文字は既定値
const value = raw.trim().toLowerCase();
if (TRUE_VALUES.has(value)) return true;
if (FALSE_VALUES.has(value)) return false;
throw new Error(`${name} は真偽値として解釈できない: ${raw}`);
}
// DEBUG=false で実行した場合
console.log(Boolean(process.env.DEBUG)); // true(空でない文字列はすべて真)
console.log(envBool("DEBUG")); // false
console.log(envBool("VERBOSE", true)); // 未設定なら trueBoolean() や !! で変換すると、"false" も空でない文字列なので true になる。
Node.js には文字列を真偽値に解釈する標準関数が無いため、受け付ける値を Set で決めて比較し、それ以外は例外にして設定ミスに気づけるようにする。
うまくいかない時: GitHub Actions の if 条件どおりにステップを実行できない
TypeScript 実行確認済み
const TRUE_VALUES: ReadonlySet<string> = new Set(["1", "true", "yes", "on"]);
const FALSE_VALUES: ReadonlySet<string> = new Set(["0", "false", "no", "off"]);
function envBool(name: string, defaultValue = false): boolean {
const raw: string | undefined = process.env[name];
if (raw === undefined || raw.trim() === "") return defaultValue; // 未設定・空文字は既定値
const value = raw.trim().toLowerCase();
if (TRUE_VALUES.has(value)) return true;
if (FALSE_VALUES.has(value)) return false;
throw new Error(`${name} は真偽値として解釈できない: ${raw}`);
}
// DEBUG=TRUE で実行した場合
console.log(process.env.DEBUG === "true"); // false(大文字の TRUE を取りこぼす)
const debug: boolean = envBool("DEBUG");
console.log(debug); // true
// const bad: boolean = process.env.DEBUG; // string | undefined は boolean に代入できないprocess.env の値は string | undefined 型なので、そのまま boolean の変数に代入するとコンパイルエラーになる。
=== "true" だけで判定すると TRUE や 1 を黙って false にしてしまうため、正規化してから比較する関数を 1 つ用意し、戻り値を boolean にそろえる。
Go 実行確認済み
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func envBool(name string, def bool) (bool, error) {
raw, ok := os.LookupEnv(name)
if !ok || strings.TrimSpace(raw) == "" {
return def, nil // 未設定・空文字は既定値
}
// ParseBool が受け付けるのは 1 t T TRUE true True 0 f F FALSE false False だけ
v, err := strconv.ParseBool(strings.TrimSpace(raw))
if err != nil {
return def, fmt.Errorf("%s は真偽値として解釈できない: %w", name, err)
}
return v, nil
}
func main() {
// DEBUG=false、ENABLE_CACHE=yes で実行した場合
debug, err := envBool("DEBUG", false)
fmt.Println(debug, err) // false <nil>
cache, err := envBool("ENABLE_CACHE", false)
fmt.Println(cache, err)
// false ENABLE_CACHE は真偽値として解釈できない: strconv.ParseBool: parsing "yes": invalid syntax
}strconv.ParseBool が受け付けるのは 1・t・T・TRUE・true・True と 0・f・F・FALSE・false・False だけで、yes や on、tRuE のような大文字小文字の混在はエラーになる。
前後の空白も許さないので、strings.TrimSpace で取り除いてから渡す。
Rust 実行確認済み
use std::env;
fn env_bool(name: &str, default: bool) -> Result<bool, String> {
match env::var(name) {
Err(env::VarError::NotPresent) => Ok(default), // 未設定は既定値
Err(e) => Err(format!("{name}: {e}")),
Ok(raw) => match raw.trim().to_ascii_lowercase().as_str() {
"" => Ok(default),
"1" | "true" | "yes" | "on" => Ok(true),
"0" | "false" | "no" | "off" => Ok(false),
_ => Err(format!("{name} は真偽値として解釈できない: {raw:?}")),
},
}
}
fn main() {
// str::parse::<bool> が受け付けるのは小文字の "true" と "false" だけ
println!("{:?}", "True".parse::<bool>()); // Err(ParseBoolError)
// DEBUG=False で実行した場合
println!("{:?}", env_bool("DEBUG", true)); // Ok(false)
println!("{:?}", env_bool("VERBOSE", true)); // 未設定なら Ok(true)
}str の parse::<bool> が受け付けるのは小文字の "true" と "false" だけで、True や 1 は Err になる。
環境変数では表記が揺れるため、to_ascii_lowercase で正規化してから match で受け付ける値を列挙し、未設定(VarError::NotPresent)と不正な値を区別して扱う。
Java 実行確認済み
import java.util.Locale;
import java.util.Set;
public class Main {
static final Set<String> TRUE_VALUES = Set.of("1", "true", "yes", "on");
static final Set<String> FALSE_VALUES = Set.of("0", "false", "no", "off");
static boolean envBool(String name, boolean defaultValue) {
String raw = System.getenv(name);
if (raw == null || raw.isBlank()) return defaultValue; // 未設定・空文字は既定値
String value = raw.strip().toLowerCase(Locale.ROOT);
if (TRUE_VALUES.contains(value)) return true;
if (FALSE_VALUES.contains(value)) return false;
throw new IllegalArgumentException(name + " は真偽値として解釈できない: " + raw);
}
public static void main(String[] args) {
// Boolean.parseBoolean は "true"(大文字小文字は無視)以外をすべて false にする
System.out.println(Boolean.parseBoolean("TRUE")); // true
System.out.println(Boolean.parseBoolean("yes")); // false(例外にならない)
System.out.println(Boolean.parseBoolean(null)); // false
// DEBUG=yes で実行した場合
System.out.println(envBool("DEBUG", false)); // true
}
}Boolean.parseBoolean は例外を投げず、大文字小文字を無視した "true" 以外をすべて false にするため、yes や 1、タイプミスの ture が黙って false になる。
自前で判定するときは、実行環境のロケールで結果が変わらないよう toLowerCase に Locale.ROOT を渡す。
C# 実行確認済み
using System;
static bool EnvBool(string name, bool defaultValue)
{
string? raw = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrWhiteSpace(raw)) return defaultValue; // 未設定・空文字は既定値
// bool.TryParse が受け付けるのは "true" と "false"(大文字小文字と前後の空白は無視)
if (bool.TryParse(raw, out bool value)) return value;
return raw.Trim().ToLowerInvariant() switch
{
"1" or "yes" or "on" => true,
"0" or "no" or "off" => false,
_ => throw new FormatException($"{name} は真偽値として解釈できない: {raw}"),
};
}
Console.WriteLine(bool.TryParse("1", out _)); // False("1" は受け付けない)
// DEBUG=" False " で実行した場合
Console.WriteLine(EnvBool("DEBUG", true)); // False
Console.WriteLine(EnvBool("VERBOSE", true)); // 未設定なら Truebool.TryParse は大文字小文字と前後の空白を無視して "true" と "false" を受け付けるが、"1" や "yes" では false を返して失敗する。
TryParse で解釈できなかった値だけを switch で追加判定すると、標準の表記と慣用の表記を両方扱える。
つまずき
環境変数の値は常に文字列で渡るため、DEBUG=false と設定しても受け取る側では "false" という 5 文字の文字列になる。
Python の bool() や JavaScript の Boolean() は空でない文字列をすべて真と評価するので、if os.getenv("DEBUG"): のような書き方では false を指定してもデバッグモードが有効になる。
"0" も同じ理由で真になる。
GitHub Actions の式やシェルスクリプトから渡した値も文字列なので、真偽値として使う箇所では必ず明示的に変換する。
受け付ける値を決め、それ以外はエラーにする
true と false に加えて 1 と 0、yes と no、on と off のどれを受け付けるかはプロジェクトで決め、1 つの関数にまとめる。
受け付けない値を黙って false に倒すと、ture のようなタイプミスで機能が無効になっても気づけないため、例外やエラーで起動時に止めるほうが安全である。
未設定と空文字をどちらも既定値に倒しておくと、DEBUG= のように値を空にして渡した場合も未設定と同じ扱いになる。
標準関数が受け付ける表記は言語ごとに違う
標準で用意された変換関数は、受け付ける表記が言語ごとに異なる。
Go の strconv.ParseBool は 1 や t、TRUE まで受け付けるが yes や on は受け付けず、Rust の parse::<bool> は小文字の true と false しか受け付けない。
C# の bool.TryParse は大文字小文字と前後の空白を無視するが 1 は受け付けず、Java の Boolean.parseBoolean は true 以外をすべて false にして例外を投げない。
Python と JavaScript には相当する標準関数が無く、Python にあった distutils.util.strtobool は 3.12 で削除された。
同じ値を複数の言語のサービスで共有するなら、どの言語でも解釈が一致する小文字の true と false にそろえておくと食い違いが起きない。