以下、実際の挙動を確認したりMSDNのリファレンス読んだりプログラム書いてふむふむした結果。
確認には Windows XP SP3 を利用した。現在( Windows 8 )では、また少し仕様が変わっている可能性がある。過去( Windows 2000 以前、および Windows ME 以前)は仕様が異なっていた可能性もある。そういう意味で、正確性は保証しない。もし誤りがあったら、気軽に当記事のコメント欄やTBで指摘してほしい。
環境変数の種類
System、User、Process、Volatileの4種が在る。ただし、 Windows 95 系列にはこの内幾つかが存在しない模様。
それぞれ、以下の特徴を持つ。
- System
- OS全体で共有する環境変数。権限が無いと閲覧以外出来ない。
- User
- ユーザー(Windowsのアカウント)毎の環境変数。当然、別のユーザーの環境変数を読み書きする事は出来ない。
- Process
- そのプロセス内でのみ有効な環境変数。
- Volatile
- 一時的な環境変数。作成してから、そのマシンがシャットダウンされるまで(ログアウトではなく)の間のみ有効。
内、Process環境変数は、System、User、Volatile全てを合わせた物となる。先に挙げた順に定義され、種類の異なる同名の環境変数は、より後に挙げた物で上書きされる。つまり、System環境変数VARを"system"、User環境変数VARを"%VAR%->user"、Volatile環境変数VARを"%VAR%->volatile"とした場合、最終的なProcess環境変数VARの値は"system->user->volatile"となる。
他の環境変数を参照する環境変数の定義
出来ない。まぁ当たり前か。
> SET V1=%V2% > SET V2=hoge > SET V1 V1=%V2%
Windowsログオン時のSystem、User環境変数
環境変数設定用ダイアログウィンドウが用意されている(後述)。
これらは辞書順に定義される。前項の仕様により、他の環境変数の値を設定する環境変数はうまく設定できない事がある。
例えば、以下のUser環境変数を設定しWindowsを再起動した後で各環境変数の値を(コマンドプロンプトでECHO
コマンドを使うことで)確認すると、VAR01の値は「hoge」ではなく「%VAR02%」となっていることが確認できる。一度だけ再帰的な展開がなされるようである。
名前 | 設定値 |
---|---|
VAR01 | %VAR02% |
VAR02 | %VAR03% |
VAR03 | hoge |
VAR04 | %VAR03% |
VAR05 | %VAR04% |
> ECHO %VAR01% %VAR02% %VAR03% %VAR04% %VAR05% %VAR02% hoge hoge hoge hoge
参考
空白の扱い
環境変数の値に空白が含まれていた場合、その展開結果は一つの値と解釈されず、空白で区切られたトークンの列挙として見られる。
例えば以下の通り。
> TYPE listargs.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i; printf("length:\t%d\n", argc - 1); for(i = 1; i < argc; ++i) { printf("arg\t%d:\t%s\n", i, argv[i]); } return 0; } > gcc -o listargs.exe listargc.c > listargs.exe foo bar length: 2 arg 1: foo arg 2: bar > SET VAR=hoge piyo > listargs.exe %VAR% length: 2 arg 1: foo arg 2: bar
ダブルクォーテーションで囲めば1つの値として認識される。
> listargs.exe "%VAR%" length: 1 arg 1: foo bar
CD
など、「1つの引数しか取らない」コマンドであれば、クォートしなくてもよしなに扱ってくれる(事がある)。これは、コマンドプロンプトに限らず「ファイル名を指定して実行」でも同様である。
ちなみに、どうやらsh(bash?)スクリプトもこの方式のようだ。zshはクォートしなくても一つの値として認識する。(何かオプションがあるのかもしれない)
PATH
PATH
は特殊な環境変数だ。Windowsは、カレントディレクトリ内に見つからなかったファイルを、この環境変数に設定されたパスから探し出そうとする。「;」で区切る事で、複数のパスを記述する事が出来、その際は先頭の物ほど優先される。
必ず、System環境変数のそれとUser環境変数のそれが結合された物が各Process環境変数として設定される。
各環境変数の操作方法
WindowsのUI
Windows XP では、専用のダイアログウィンドウが用意されている。
「スタート」メニューの「コントロール パネル」サブメニュー内の「システム」で「システムのプロパティ」ダイアログウィンドウを表示し、それの「詳細設定」タブ内「環境変数」ボタンで表示される「環境変数」ダイアログウィンドウで、System環境変数、User環境変数がそれぞれ設定、確認可能。
Windows 95 系列には、専用のUIは用意されていない。
記憶領域
NT系列のWindowsでは、System環境変数、User環境変数はレジストリに記録される。Process環境変数、Volatile環境変数についての記述は発見できなかったが、性質から言ってオンメモリだと思われる。
System | HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment |
---|---|
User | HKEY_CURRENT_USER\Environment |
95系列のWindowsでは、C:\AUTOEXEC.BATファイル内でSET
コマンドを用いて設定する。
Win32 API
Process環境変数用の物のみAPIが用意されている。System、User環境変数は直接レジストリを参照/編集する必要がある。
また、文字列中の環境変数を展開する為のAPIも用意されている。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tchar.h>
#define BUF_SIZE 1024
void set_and_get(void) {
_TCHAR buf[BUF_SIZE];
// (環境変数FOOの値を"BAR"に)設定
SetEnvironmentVariable(_T("FOO"), "BAR");
// (環境変数FOOの値を)取得
GetEnvironmentVariable(_T("FOO"), buf, BUF_SIZE);
_putts(buf);
}
void block(void) {
LPTSTR block;
// 環境変数ブロックを取得
block = GetEnvironmentStrings();
while(*block != '\0') {
_putts(block);
block += _tcslen(block) + 1;
}
// 環境変数ブロックを解放
FreeEnvironmentStrings(block);
}
void expand(void) {
_TCHAR buf[BUF_SIZE];
// 環境変数を含む文字列を展開する
ExpandEnvironmentStrings(_T("FOO variable: %FOO%"), buf, BUF_SIZE);
_putts(buf);
}
レジストリ編集によりSystem、User環境変数を変更した場合は、それをWindowsに通知する必要がある。
#include <windows.h>
#include <tchar.h>
void notify(void) {
HKEY hkey;
const _TCHAR value[] = _T("VALUE");
DWORD dwReturnValue;
// (レジストリに直接書き込む事で)User環境変数HOGEを作る
RegOpenKeyEx(HKEY_CURRENT_USER, _T("Environment"), 0, KEY_SET_VALUE, &hkey);
RegSetValueEx(hkey, _T("HOGE"), 0, REG_EXPAND_SZ, (BYTE *)value, (_tcslen(value) + 1) * sizeof(_TCHAR));
// システムに環境変数の変更を通知する
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);
RegCloseKey(hkey);
}
参考
- GetEnvironmentVariable 関数
- SetEnvironmentVariable 関数
- GetEnvironmentStrings 関数
- FreeEnvironmentStrings 関数
- ExpandEnvironmentStrings 関数
- 環境変数の表示と環境変数ブロックの仕組み - Web/DB プログラミング徹底解説
- 環境変数をシステムに通知する方法
.NET Framework
.NET Framework 2.0より可能。System、User、Process各環境変数の取得/設定ができる。Volatile環境変数用の物は用意されていない。
// C#での例
class TestEnv {
static void set_and_get() {
// Process環境変数FOOの値を"BAR"に設定
System.Environment.SetEnvironmentVariable("FOO", "BAR", System.EnvironmentVariableTarget.Process);
// Processの場合は第3引数を省略できる
// System.SetEnvironmentVariable("FOO", "BAR");
// Process環境変数FOOの値を取得
string buf = System.Environment.GetEnvironmentVariable("FOO", System.EnvironmentVariableTarget.Process);
// Processの場合は第2引数を省略できる
// string buf = System.GetEnvironmentVariable("FOO");
System.Console.WriteLine(buf);
}
static void all() {
// 全Process環境変数の名前とその値を取得
System.Collections.IDictionary vars = System.Environment.GetEnvironmentVariables(System.EnvironmentVariableTarget.Process);
// Processの場合は引数を省略できる
// IDictionary vars = System.Environment.GetEnvironmentVariables();
foreach(System.Collections.DictionaryEntry i in vars) {
System.Console.WriteLine("{0}: {1}", i.Key, i.Value);
}
}
static void expand() {
// 環境変数を含む文字列を展開する
string buf = System.Environment.ExpandEnvironmentVariables("FOO variable: %FOO%");
System.Console.WriteLine(buf);
}
}
参考
- Environment.GetEnvironmentVariable メソッド (System)
- Environment.SetEnvironmentVariable メソッド (System)
- Environment.GetEnvironmentVariables メソッド (System)
- Environment.ExpandEnvironmentVariables メソッド (System)
- EnvironmentVariableTarget 列挙体 (System)
コマンドプロンプト
定義できるのはProcess環境変数のみ。
(Vista以降であればSETX
というコマンドでUser及びSystem環境変数が設定できるらしいが未確認。)
> REM 環境変数FOOの値を"BAR"に設定
> SET FOO=BAR
> REM 環境変数FOOの値を表示
> SET FOO
FOO=BAR
> REM 環境変数の一覧を表示
> SET
ALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\AmaiSaeta\Application Data
CLIENTNAME=Console
CommonProgramFiles=C:\Program Files\Common Files
COMPUTERNAME=MYCOMPUTER
ComSpec=C:\WINDOWS\system32\cmd.exe
(長いので省略)
> REM 環境変数を含む文字列を展開(して、出力)
> ECHO FOO variable: %FOO%
FOO Variable: BAR
> REM PATHについては以下の方法でも可
> REM 設定
> PATH C:\foo
> REM 表示
> PATH
PATH=C:\foo
参考
Windows Scripting Host
System、User、Volatile各環境変数の取得設定が可能。Process環境変数は取得のみ可能で設定は出来ない。設定しようとしても何のエラーも発生しないので注意が必要。
// JScriptでの例
function set_and_get() {
var sh, env, buf;
// User環境変数のコレクションを取得
sh = WScript.CreateObject('WScript.Shell');
envs = sh.Environment('User');
// User環境変数FOOの値を"BAR"に設定
envs('FOO') = 'BAR';
// User環境変数FOOの値を取得
buf = envs('FOO');
WScript.Echo(buf);
}
function all() {
var envs = WScript.CreateObject('WScript.Shell').Environment('User');
var buf = [];
// VBSであれば単純に For Each 出来るが、JScriptではEnumeratorオブジェクトを使う必要がある。
for(var i = new Enumerator(envs); !i.atEnd(); i.moveNext()) {
buf.push(i.item());
}
WScript.Echo(buf.join('\n'));
}
function expand() {
var sh = WScript.CreateObject('WScript.Shell');
var buf;
buf = sh.ExpandEnvironmentStrings('FOO variable: %FOO%');
WScript.Echo(buf);
}
参考
Windows PowerShell
PowerShellには、さながらファイルシステムのドライブ(Cドライブ、Dドライブ……)のように各種データ群へアクセス出来る"ドライブ"という機能があり、環境変数へはEnvドライブからアクセスする。
また、変数名の前に"$env:"を前置することで、値を参照することも可能。
おそらく、Process環境変数のみアクセス可能。
> # 環境変数FOOの値を"BAR"に設定
> Set-Item -Path Env:FOO -Value "BAR"
> # 同上
> Set-Item Env:FOO "BAR"
> # 同上
> $env:FOO = "BAR"
> # 環境変数FOOの値を取得
> $var = (Get-Item env:FOO).Value
> # 同上
> $var = $env:FOO
> $v
BAR
> # 環境変数の一覧を取得
> $vars = (Get-ChildItem Env:)
> $vars
Name Value
---- -----
ALLUSERSPROFILE C:\Documents and Settings\All Users
APPDATA C:\Documents and Settings\AmaiSaeta\Application Data
CLIENTNAME Console
CommonProgramFiles C:\Program Files\Common Files
COMPUTERNAME MYCOMPUTER
ComSpec C:\WINDOWS\system32\cmd.exe
(長いので省略)
参考
- PowerShellの "
Get-Help about_environment_variables
" コマンド
- System、User、Process、Volatile
- MSDN内
WshShell.Environment
の引数より。正式には何というのかは不明。当記事ではこの表記で統一している。 - Windowsに通知する必要がある。
- という事になっている筈だが、どうも通知しなくても問題ないように見える。
今回の検証で、初めてC#に触れたが、一番最初の用途がこれってのは、それはそれでどうなんだろう……w