Environment variables store system and user configuration values that Windows and applications use to locate resources, configure behavior, and pass information between processes.

1. Overview#

Environment variables are name-value pairs maintained by the operating system and available to all running processes. They serve as a universal configuration mechanism, providing paths to system directories, user profile locations, processor information, and custom application settings.

Windows uses three scopes for environment variables:

  • System - apply to all users and services; stored in the registry
  • User - specific to the logged-in user; stored in the registry
  • Process - temporary, exist only for the lifetime of the current process and its children

2. Scope and Precedence#

When a process starts, it inherits a merged set of environment variables. If the same variable name exists in multiple scopes, the effective value follows this precedence (highest to lowest):

  1. Process - set at runtime; overrides everything
  2. User - set per user account
  3. System - machine-wide defaults

Note: The PATH variable is special. System and User PATH values are concatenated (system first, then user), not overridden.

Registry Locations#

ScopeRegistry Path
SystemHKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
UserHKCU:\Environment

Changes to registry-based variables require a new process or a logoff/restart to take effect for existing processes. You can broadcast a WM_SETTINGCHANGE message to notify applications of the change.

3. Viewing Variables#

PowerShell#

# List all environment variables
Get-ChildItem Env:

# Get a specific variable
$env:APPDATA
$env:PATH

# Filter variables by pattern
Get-ChildItem Env: | Where-Object { $_.Name -like "*PROGRAM*" }

# View system vs user PATH separately
[Environment]::GetEnvironmentVariable("PATH", "Machine")
[Environment]::GetEnvironmentVariable("PATH", "User")

Command Prompt (CMD)#

:: List all environment variables
set

:: Show a specific variable
echo %APPDATA%

:: Show PATH
echo %PATH%

GUI#

  1. Press Win + R, type sysdm.cpl, press Enter
  2. Go to the Advanced tab
  3. Click Environment Variables

4. Setting and Modifying Variables#

PowerShell#

# Set a process-scoped variable (temporary, current session only)
$env:MY_VAR = "value"

# Set a user-scoped variable (persistent)
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")

# Set a system-scoped variable (persistent, requires admin)
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "Machine")

# Remove a variable (set to null)
[Environment]::SetEnvironmentVariable("MY_VAR", $null, "User")

Command Prompt (CMD)#

:: Set a process-scoped variable (current session only)
set MY_VAR=value

:: Set a persistent user variable
setx MY_VAR "value"

:: Set a persistent system variable (requires admin)
setx MY_VAR "value" /M

:: Remove a variable
set MY_VAR=

Note: setx has a 1024-character limit for variable values. For longer values (especially PATH), use PowerShell or the GUI.

Registry (Direct)#

# Set a system variable via registry
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" `
    -Name "MY_VAR" -Value "value"

# Set a user variable via registry
Set-ItemProperty -Path "HKCU:\Environment" -Name "MY_VAR" -Value "value"

Broadcast Change Notification#

After modifying registry-based variables, notify running applications:

# Send WM_SETTINGCHANGE to notify applications
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
    IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
    uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
$result = [UIntPtr]::Zero
[Win32.NativeMethods]::SendMessageTimeout(
    [IntPtr]0xFFFF, 0x001A, [UIntPtr]::Zero, "Environment",
    0x0002, 5000, [ref]$result) | Out-Null

5. PATH Management#

The PATH variable tells Windows where to find executable files. It is a semicolon-separated list of directory paths.

View PATH Entries#

# List PATH entries, one per line
$env:PATH -split ";"

# View system PATH only
([Environment]::GetEnvironmentVariable("PATH", "Machine")) -split ";"

# View user PATH only
([Environment]::GetEnvironmentVariable("PATH", "User")) -split ";"

Add to PATH#

# Add to user PATH (persistent)
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
$newPath = "$currentPath;C:\MyTools"
[Environment]::SetEnvironmentVariable("PATH", $newPath, "User")

# Add to system PATH (persistent, requires admin)
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "Machine")
$newPath = "$currentPath;C:\MyTools"
[Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine")

# Add to current session only
$env:PATH += ";C:\MyTools"

Remove from PATH#

# Remove a directory from user PATH
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
$newPath = ($currentPath -split ";" | Where-Object { $_ -ne "C:\MyTools" }) -join ";"
[Environment]::SetEnvironmentVariable("PATH", $newPath, "User")

Clean Up Duplicate Entries#

$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User")
$cleanPath = (($currentPath -split ";") | Select-Object -Unique | Where-Object { $_ -ne "" }) -join ";"
[Environment]::SetEnvironmentVariable("PATH", $cleanPath, "User")

6. Common Variables Reference#

System Directories#

VariableTypical ValueDescription
%ALLUSERSPROFILE%C:\ProgramDataCommon application data for all users
%PROGRAMDATA%C:\ProgramDataSame as ALLUSERSPROFILE
%PROGRAMFILES%C:\Program Files64-bit program installation directory
%PROGRAMFILES(X86)%C:\Program Files (x86)32-bit program installation directory
%ProgramW6432%C:\Program Files64-bit program files (even from 32-bit process)
%COMMONPROGRAMFILES%C:\Program Files\Common FilesShared program components
%COMMONPROGRAMFILES(x86)%C:\Program Files (x86)\Common FilesShared 32-bit program components
%SystemDrive%C:Drive containing the Windows directory
%SystemRoot%C:\WindowsWindows installation directory
%WINDIR%C:\WindowsSame as SystemRoot
%COMSPEC%C:\Windows\System32\cmd.exePath to the command interpreter
%DriverData%C:\Windows\System32\Drivers\DriverDataDriver data directory

User Directories#

VariableTypical ValueDescription
%APPDATA%C:\Users\<username>\AppData\RoamingRoaming application data
%LOCALAPPDATA%C:\Users\<username>\AppData\LocalLocal application data
%USERPROFILE%C:\Users\<username>Current user's profile directory
%HOMEDRIVE%C:Drive letter of user's home directory
%HOMEPATH%\Users\<username>Path portion of user's home directory
%TEMP% / %TMP%C:\Users\<username>\AppData\Local\TempTemporary file directory
%USERNAME%<username>Current user's login name
%PUBLIC%C:\Users\PublicPublic user profile
%OneDrive%C:\Users\<username>\OneDriveOneDrive sync folder

System Information#

VariableTypical ValueDescription
%COMPUTERNAME%<hostname>Machine hostname
%USERDOMAIN%<domain>Domain of the current user
%USERDOMAIN_ROAMINGPROFILE%<domain>Domain associated with roaming profile
%LOGONSERVER%\\<server>Domain controller that authenticated the user
%OS%Windows_NTOperating system identifier
%PROCESSOR_IDENTIFIER%(varies)Processor description string
%PROCESSOR_LEVEL%(varies)Processor family level
%PROCESSOR_REVISION%(varies)Processor revision number
%NUMBER_OF_PROCESSORS%(varies)Count of logical processors

Shell and Path#

VariableTypical ValueDescription
%PATH%(semicolon-separated dirs)Executable search path
%PathExt%.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.mscFile extensions treated as executable
%PROMPT%$P$GCMD prompt format string
%PSModulePath%(semicolon-separated dirs)PowerShell module search path

CMD-Only Dynamic Variables#

These variables are only available in Command Prompt sessions and are evaluated dynamically:

VariableDescription
%CD%Current working directory
%DATE%Current date
%TIME%Current time
%RANDOM%Random number (0 through 32767)
%ERRORLEVEL%Exit code of the previous command
%CMDCMDLINE%Command line used to launch the CMD session
%CMDEXTVERSION%Command processor extensions version number

7. Using Variables in Scripts#

In PowerShell#

# Access environment variables
$appData = $env:APPDATA
$home = $env:USERPROFILE

# Use in paths
$logFile = Join-Path $env:TEMP "myapp.log"

# Expand variables in strings (double quotes only)
Write-Host "User profile is at $env:USERPROFILE"

# Access via the Env: drive
Get-Item Env:COMPUTERNAME
Set-Item Env:MY_VAR -Value "test"
Remove-Item Env:MY_VAR

In CMD Batch Files#

:: Access variables with percent signs
echo %USERNAME% on %COMPUTERNAME%

:: Set and use in the same script
set LOGDIR=%TEMP%\logs
mkdir %LOGDIR%

:: Delayed expansion (inside loops)
setlocal enabledelayedexpansion
for /L %%i in (1,1,5) do (
    set COUNT=%%i
    echo !COUNT!
)
endlocal

In Application Configuration#

Many applications and frameworks use environment variables for configuration:

# Set Java home
[Environment]::SetEnvironmentVariable("JAVA_HOME", "C:\Program Files\Java\jdk-21", "Machine")

# Set Node.js options
[Environment]::SetEnvironmentVariable("NODE_OPTIONS", "--max-old-space-size=4096", "User")

# Set proxy for development tools
[Environment]::SetEnvironmentVariable("HTTP_PROXY", "http://<proxy-host>:<port>", "User")
[Environment]::SetEnvironmentVariable("HTTPS_PROXY", "http://<proxy-host>:<port>", "User")

Troubleshooting#

IssueCauseSolution
Variable change not visible in open terminalsExisting processes have stale environmentOpen a new terminal; or broadcast WM_SETTINGCHANGE
setx truncates PATHPATH exceeds 1024-character setx limitUse PowerShell [Environment]::SetEnvironmentVariable() or the GUI instead
Application cannot find executableDirectory not in PATHAdd the directory to PATH and open a new terminal
User variable overrides system variableSame name exists in both scopesRename or remove one; remember PATH is concatenated, not overridden
%VARIABLE% prints literally in CMDVariable does not existVerify spelling and scope; use set to list all variables
$env:VAR is empty in PowerShellVariable not set for current scopeCheck with [Environment]::GetEnvironmentVariable("VAR", "Machine") and "User" separately
Changes lost after rebootVariable was set with $env: or set (process-scoped only)Use setx, [Environment]::SetEnvironmentVariable(), or the GUI for persistence

See Also#

Sources#