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

Table of Contents#

  1. Overview
  2. Scope and Precedence
  3. Viewing Variables
  4. Setting and Modifying Variables
  5. PATH Management
  6. Common Variables Reference
  7. Using Variables in Scripts
  8. Troubleshooting
  9. See Also
  10. Sources

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#