cheat sheet
set
Set, display, and delete cmd.exe environment variables within the current session, perform integer arithmetic with /A, and read interactive user input with /P.
set — Environment Variables
What it is
set is a built-in cmd.exe command that manages environment variables for the current command-prompt session. It can display all variables, create or update a variable, delete a variable, perform integer arithmetic (/A), and prompt the user for input (/P). Changes made with set last only for the lifetime of the current cmd.exe process — they do not persist after the window closes. To make a variable permanent (user- or system-wide), use setx instead.
Availability
set is built into cmd.exe on every Windows version. PowerShell equivalents: $env:VAR, [System.Environment]::SetEnvironmentVariable().
set /?
Output:
Displays, sets, or removes cmd.exe environment variables.
SET [variable=[string]]
SET [variable]
SET /A expression
SET /P variable=[promptString]
Syntax
set [variable[=value]]
set /A expression
set /P variable=[prompt]
Output: (varies by form — see sections below)
Essential options
| Form | Meaning |
|---|---|
set | List all environment variables |
set PREFIX | List all variables whose name starts with PREFIX |
set VAR=value | Create or update VAR |
set VAR= | Delete VAR (empty right-hand side) |
set /A VAR=expr | Evaluate integer arithmetic expression and assign |
set /P VAR=prompt | Display prompt and read one line from stdin into VAR |
Listing variables
Running set with no arguments prints every variable in the current environment in NAME=value form. Providing a prefix filters to matching names.
set
Output:
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\alicedev\AppData\Roaming
COMPUTERNAME=MYHOST
ComSpec=C:\Windows\system32\cmd.exe
...
set path
Output:
Path=C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;...
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
Creating and updating variables
set VAR=value assigns the string to the right of = verbatim — no quotes required (and including quotes makes them part of the value). Variable names are case-insensitive.
set GREETING=Hello, world!
echo %GREETING%
Output:
Hello, world!
set PROJECT=myapp
set BASE=C:\Projects
set OUTDIR=%BASE%\%PROJECT%\dist
echo %OUTDIR%
Output:
C:\Projects\myapp\dist
Deleting a variable
Assigning an empty value removes the variable from the environment. There must be nothing after the =.
set GREETING=
set GREETING
Output:
Environment variable GREETING not defined
Arithmetic with /A
/A evaluates a C-style integer expression and stores the result. Supports +, -, *, /, % (modulo), <<, >>, &, |, ^, ~, unary -. Variables inside the expression do not need % delimiters.
set /A RESULT=7 * 6
echo %RESULT%
Output:
42
set COUNT=10
set /A COUNT=COUNT+1
echo %COUNT%
Output:
11
set /A HEX=0xFF
echo %HEX%
Output:
255
Interactive input with /P
/P prints a prompt string (without a newline) then pauses until the user presses Enter, storing everything typed into the variable.
set /P NAME=Enter your name:
echo Hello, %NAME%!
Output:
Enter your name: Alice Dev
Hello, Alice Dev!
Substring expansion
cmd.exe supports substring slicing via %VAR:~start,length%. Negative offsets count from the end. This is useful for extracting date parts from %DATE% or %TIME%.
set FULL=HelloWorld
echo %FULL:~0,5%
Output:
Hello
echo %FULL:~-5%
Output:
World
rem Extract year-month from %DATE% (depends on locale)
set YM=%DATE:~-4,4%-%DATE:~-10,2%
echo %YM%
Output:
2026-04
Search-and-replace expansion
%VAR:find=replace% substitutes all occurrences of find with replace in the variable's value.
set PATH_UNIX=/home/alice/projects/myapp
set PATH_WIN=%PATH_UNIX:/=\%
echo %PATH_WIN%
Output:
\home\alice\projects\myapp
rem Strip spaces from a variable
set PADDED= hello
set TRIMMED=%PADDED: =%
echo [%TRIMMED%]
Output:
[hello]
Variable scope and setx
Changes made with set apply only to the current session; child processes inherit a copy at launch but cannot propagate changes back. setx writes to the registry for persistence across sessions.
rem Permanent user-level variable
setx MY_TOOL_HOME "C:\Tools\mytool"
Output:
SUCCESS: Specified value was saved.
Common pitfalls
- Trailing spaces in
set VAR=value— the space before the newline is included in the value; avoid them or quote the assignment. - Quotes become part of the value —
set VAR="hello"stores"hello"with the quotes; drop the outer quotes for a bare string. setchanges are session-scoped — closing the window discards them; usesetxfor persistence./Auses integer arithmetic only — floating-point is not supported; use PowerShell or a helper script for decimals.%VAR%expansion at parse time — inside aniforforblock, the value is captured when the block is parsed, not when the line runs. Usesetlocal enabledelayedexpansionand!VAR!for runtime expansion.set PREFIXlists, not reads —set PATHprints all variables beginning withPATH, not just%PATH%; useecho %PATH%for a single variable.
Real-world recipes
Build a timestamped output path
set /A YEAR=%DATE:~-4,4%
rem Or use substring approach:
set STAMP=%DATE:~-4,4%%DATE:~-10,2%%DATE:~-7,2%
set OUTFILE=C:\Reports\report_%STAMP%.csv
echo Output: %OUTFILE%
Output:
Output: C:\Reports\report_20260428.csv
Interactive confirmation prompt in a batch script
@echo off
set /P CONFIRM=Delete all temp files? [y/N]:
if /i "%CONFIRM%"=="y" (
del /Q /F C:\Temp\*
echo Deleted.
) else (
echo Aborted.
)
Output:
Delete all temp files? [y/N]: n
Aborted.
Accumulate a counter across a loop
@echo off
setlocal enabledelayedexpansion
set COUNT=0
for %%f in (C:\Logs\*.log) do (
set /A COUNT+=1
)
echo Found %COUNT% log files.
Output:
Found 7 log files.
Temporarily override PATH for a single build
@echo off
set OLD_PATH=%PATH%
set PATH=C:\Tools\mingw64\bin;%PATH%
gcc -o build\hello src\hello.c
set PATH=%OLD_PATH%
echo PATH restored.
Output:
PATH restored.
set vs setx vs registry edits
Three tools touch environment variables, each with a different scope and persistence model. Confusing them is the most common source of "I set it but it disappeared" frustration.
| Tool | Scope | Persistence | Takes effect |
|---|---|---|---|
set VAR=value | Current cmd.exe only | Lifetime of the shell | Immediately, this shell |
setx VAR value | Current user (default) | Permanent (registry) | New shells only |
setx VAR value /M | System-wide | Permanent (registry) | New shells; requires admin |
reg add HKCU\Environment ... | Current user | Permanent (registry) | New shells; needs broadcast |
[Environment]::SetEnvironmentVariable(name, value, "User") | Current user (PowerShell) | Permanent | New shells only |
[Environment]::SetEnvironmentVariable(name, value, "Machine") | System-wide | Permanent | New shells; needs admin |
[Environment]::SetEnvironmentVariable(name, value, "Process") | Current process | This process only | Immediately |
The most important nuance: setx never affects the current shell. After setx FOO=bar, the original cmd.exe window still has the old value (or no value). Spawn a new shell to see the change.
setx GREETING "Hello permanent"
echo %GREETING%
Output:
SUCCESS: Specified value was saved.
(%GREETING% is still empty in this shell — open a new cmd to see it)
rem In a new cmd window
echo %GREETING%
Output:
Hello permanent
setx value-length limit
setx truncates values longer than 1024 characters with no warning, which is a notorious problem when extending PATH blindly. Always check %PATH% length before piping it through setx.
echo %PATH% | find /c /v ""
Output:
1
(Hint: the character count is in %PATH% itself; use PowerShell for precision.)
($env:PATH).Length
Output:
1872
For PATH editing, prefer PowerShell [Environment]::SetEnvironmentVariable("Path", ..., "User") which has no length cap, or edit via "System Properties → Environment Variables" GUI.
# Safe permanent PATH append without truncation
$old = [Environment]::GetEnvironmentVariable("Path","User")
$new = $old + ";C:\Tools\mingw64\bin"
[Environment]::SetEnvironmentVariable("Path", $new, "User")
Output: (no console output; PATH updated for current user)
PowerShell equivalents
PowerShell does not use set at all (which is reserved for Set-Variable, a different concept). Reading and writing environment variables uses the $env: drive or the static [Environment] class. Knowing both forms matters because they have different scopes.
| cmd / batch | PowerShell |
|---|---|
set VAR=value | $env:VAR = "value" |
set VAR= | Remove-Item Env:\VAR |
echo %VAR% | $env:VAR or Write-Output $env:VAR |
set (list all) | Get-ChildItem Env: or dir env: |
set PREFIX (filter) | Get-ChildItem Env:PREFIX* |
setx VAR value | [Environment]::SetEnvironmentVariable("VAR","value","User") |
setx VAR value /M | [Environment]::SetEnvironmentVariable("VAR","value","Machine") |
set /A EXPR | $result = expr (full PowerShell arithmetic) |
set /P VAR=Prompt: | $VAR = Read-Host "Prompt" |
set "VAR=value with spaces" | $env:VAR = "value with spaces" |
%VAR:~0,5% | $env:VAR.Substring(0,5) |
%VAR:find=replace% | $env:VAR -replace 'find','replace' |
# Set a session variable
$env:GREETING = "Hello, world!"
$env:GREETING
Output:
Hello, world!
# Permanent user-level (writes registry; needs new shell to see)
[Environment]::SetEnvironmentVariable("GREETING", "Hello permanent", "User")
Output: (no output)
# Permanent machine-level (requires Administrator)
[Environment]::SetEnvironmentVariable("GREETING", "Hello system", "Machine")
Output: (no output; needs admin)
# Read all environment variables
Get-ChildItem Env: | Sort-Object Name
Output:
Name Value
---- -----
ALLUSERSPROFILE C:\ProgramData
APPDATA C:\Users\alicedev\AppData\Roaming
...
# Remove a variable
Remove-Item Env:\GREETING
Output: (no output)
The three scopes of [Environment]::SetEnvironmentVariable
The Target argument controls where the value is stored. Picking the wrong one is the cause of most "I set it but it didn't stick" or "I set it and now PATH is duplicated" issues.
| Target | Storage | Affects | Admin required |
|---|---|---|---|
"Process" | Current process memory only | This PowerShell session only | No |
"User" | HKCU\Environment registry key | New shells of this user | No |
"Machine" | HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment | New shells of any user | Yes |
# Three calls, three different effects:
[Environment]::SetEnvironmentVariable("API_KEY", "secret", "Process")
[Environment]::SetEnvironmentVariable("API_KEY", "secret", "User")
[Environment]::SetEnvironmentVariable("API_KEY", "secret", "Machine")
Output: (the third throws if not run as admin)
After a User or Machine update, currently running processes do not see the new value — they keep their inherited copy. To refresh without logging off, broadcast a WM_SETTINGCHANGE message:
# Force Explorer and new shells to pick up the change immediately
$signature = '[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);'
$type = Add-Type -MemberDefinition $signature -Name 'Win32SendMessage' -Namespace 'Win32Functions' -PassThru
$HWND_BROADCAST = [IntPtr]0xffff
$WM_SETTINGCHANGE = 0x1a
$result = [UIntPtr]::Zero
$type::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::Zero, "Environment", 2, 5000, [ref]$result)
Output: (no visible output; future shells and Explorer see the new value)
Inspecting the registry directly
Environment variables stored permanently live under two registry keys. Reading them with reg is sometimes faster than firing up PowerShell.
rem User-level variables
reg query HKCU\Environment
rem System-level variables
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
Output:
HKEY_CURRENT_USER\Environment
Path REG_EXPAND_SZ C:\Users\alicedev\AppData\Local\Programs\Python\Python313;...
GREETING REG_SZ Hello permanent
...
rem Read a single user var
reg query HKCU\Environment /v GREETING
Output:
HKEY_CURRENT_USER\Environment
GREETING REG_SZ Hello permanent
rem Write directly to the registry (does not broadcast WM_SETTINGCHANGE)
reg add HKCU\Environment /v FOO /t REG_SZ /d "bar" /f
Output:
The operation completed successfully.
Note: reg add skips the WM_SETTINGCHANGE broadcast, so other running apps will not see the change until restart. Always prefer setx or [Environment]::SetEnvironmentVariable for the broadcast.
Automatic / dynamic environment variables
cmd.exe exposes several "magic" variables that are recomputed each time they are read. They are not stored anywhere — they are evaluated by the command processor itself.
| Variable | Value |
|---|---|
%CD% | Current directory (full path) |
%DATE% | Current date (locale-formatted) |
%TIME% | Current time |
%RANDOM% | Random integer 0-32767 |
%ERRORLEVEL% | Exit code of last command |
%CMDEXTVERSION% | Cmd extensions version |
%CMDCMDLINE% | The original command line that started cmd |
%HIGHESTNUMANODENUMBER% | Highest NUMA node number |
echo Current dir: %CD%
echo Time now: %TIME%
echo Random: %RANDOM%
Output:
Current dir: C:\Users\alicedev\Projects\myapp
Time now: 14:22:05.31
Random: 27842
Each call to %RANDOM% returns a different value, which is useful for unique temp filenames:
set TMP_FILE=C:\Temp\work_%RANDOM%_%RANDOM%.tmp
echo Using %TMP_FILE%
Output:
Using C:\Temp\work_27842_15193.tmp
Variable expansion edge cases
%VAR% expansion happens at parse time. Inside for, if, and parenthesised blocks, the value is frozen when the block is parsed, not when each line executes. This is the single biggest source of batch-script bugs.
@echo off
set X=0
for /L %%i in (1,1,3) do (
set /A X+=1
echo X is now %X%
)
echo Final X is %X%
Output:
X is now 0
X is now 0
X is now 0
Final X is 3
The fix is setlocal enabledelayedexpansion plus !X! instead of %X%:
@echo off
setlocal enabledelayedexpansion
set X=0
for /L %%i in (1,1,3) do (
set /A X+=1
echo X is now !X!
)
echo Final X is !X!
Output:
X is now 1
X is now 2
X is now 3
Final X is 3
Quoting tricks for whitespace and special characters
To assign a value containing trailing whitespace, equals signs, or special characters, quote both the name and value together:
rem Wrong — quotes are part of the value
set NAME="Alice"
echo "[%NAME%]"
Output:
["Alice"]
rem Right — the entire assignment is quoted
set "NAME=Alice"
echo [%NAME%]
Output:
[Alice]
rem Trailing spaces preserved
set "PROMPT_TEXT=Enter: "
set /P RESPONSE=%PROMPT_TEXT%
Output:
Enter:
The "VAR=value" form is the safest for assignments containing spaces, equals signs, or trailing whitespace — cmd.exe parses the quotes as delimiters and does not include them in the value.
setlocal and endlocal
setlocal introduces a new variable scope; endlocal pops it. Any set calls between them are discarded when endlocal executes (or the script ends). This is essential for batch helpers that should not leak variables to the caller.
@echo off
set OUTSIDE=before
setlocal
set OUTSIDE=changed
set INSIDE=inner
echo Inside: OUTSIDE=%OUTSIDE%, INSIDE=%INSIDE%
endlocal
echo Outside: OUTSIDE=%OUTSIDE%, INSIDE=[%INSIDE%]
Output:
Inside: OUTSIDE=changed, INSIDE=inner
Outside: OUTSIDE=before, INSIDE=[]
To leak a single value out of a setlocal block, use the for /F trick:
@echo off
setlocal
set COMPUTED=hello-%RANDOM%
(endlocal & set RESULT=%COMPUTED%)
echo Result outside: %RESULT%
Output:
Result outside: hello-27842
setlocal enableextensions
Command extensions (enabled by default since Windows 2000) provide set /A, set /P, the %~ expansion modifiers, and many other features. To explicitly require them:
@echo off
setlocal enableextensions
if errorlevel 1 (
echo Extensions required — exiting.
exit /b 1
)
set /A SUM=2+2
echo %SUM%
Output:
4
To enable both extensions and delayed expansion in one line:
setlocal enableextensions enabledelayedexpansion
Output: (none — exits 0 on success)
set /A operator reference
set /A evaluates a C-style integer expression. All operators are 32-bit signed; overflow wraps. Floating-point is not supported.
| Operator | Meaning | Example |
|---|---|---|
+ - * / | Arithmetic | set /A R=10/3 → 3 |
% | Modulo | set /A R=10%%3 → 1 (in cmd, escape % as %%) |
( ) | Grouping | set /A R=(2+3)*4 → 20 |
<< >> | Bit shifts | set /A R=1<<4 → 16 |
& | Bitwise AND | set /A R=12&10 → 8 |
| ` | ` | Bitwise OR |
^ | Bitwise XOR | set /A R=12^^10 → 6 (escape ^) |
~ | Bitwise NOT | set /A R=~0 → -1 |
! | Logical NOT | set /A R=!0 → 1 |
, | Statement separator | set /A A=1, B=2, R=A+B |
= += -= *= /= %= &= ` | = ^= <<= >>=` | Compound assignment |
0x prefix | Hexadecimal literal | set /A R=0xFF → 255 |
0 prefix | Octal literal | set /A R=010 → 8 |
set /A R=(100 + 50) / 5
echo %R%
Output:
30
set /A FLAGS=0x01 ^| 0x04
echo %FLAGS%
Output:
5
Substring and replacement modifier reference
| Form | Effect |
|---|---|
%VAR% | Full value |
%VAR:~N% | Substring from offset N to end |
%VAR:~N,L% | Substring from offset N, length L |
%VAR:~-N% | Substring of last N characters |
%VAR:~N,-M% | From offset N to M characters before end |
%VAR:find=replace% | Replace all occurrences |
%VAR:*find=replace% | Replace from start up to (and including) find |
set S=HelloWorld
echo %S:~0,5%
echo %S:~5%
echo %S:~-5%
echo %S:~2,3%
echo %S:World=Friend%
Output:
Hello
World
World
llo
HelloFriend
For dynamically computed start/length, use a helper variable:
set S=abcdefghij
set /A POS=3
set /A LEN=4
call set CHUNK=%%S:~%POS%,%LEN%%%
echo %CHUNK%
Output:
defg
Argument expansion modifiers (%~)
In batch scripts and inside for /F blocks, you can apply path-component modifiers to %0, %1, ..., %9 and for loop variables (%%i, %%j).
| Modifier | Meaning |
|---|---|
%~1 | Remove surrounding quotes from %1 |
%~f1 | Full path |
%~d1 | Drive letter |
%~p1 | Path (without drive) |
%~n1 | File name |
%~x1 | Extension (with leading dot) |
%~s1 | Short (8.3) path |
%~a1 | File attributes |
%~t1 | Last-modified timestamp |
%~z1 | File size in bytes |
%~dp1 | Drive + path (most useful for "directory of the script") |
%~nx1 | Name + extension |
%~ftza1 | Combine modifiers |
@echo off
echo Script: %~nx0
echo Dir: %~dp0
echo First arg full path: %~f1
Output (if invoked as myscript.bat C:\file.txt):
Script: myscript.bat
Dir: C:\Scripts\
First arg full path: C:\file.txt
A script that needs to reliably reference files relative to itself uses %~dp0:
@echo off
set SCRIPT_DIR=%~dp0
echo Config at: %SCRIPT_DIR%config.ini
Output:
Config at: C:\Scripts\config.ini
Secrets in environment variables — the security caveat
Environment variables are visible to every child process and to anyone who can read your registry. They are not a secure secret store. For real secrets:
- Use Windows Credential Manager (
cmdkey,Get-Credential). - Use DPAPI-protected files (
ConvertTo-SecureString). - Use a dedicated secret manager like Azure Key Vault, Hashicorp Vault, 1Password CLI.
# Safer than environment variable: write encrypted to disk
"my-api-key" | ConvertTo-SecureString -AsPlainText -Force |
ConvertFrom-SecureString | Out-File "$env:USERPROFILE\.api_key.enc"
# Read back later
$key = Get-Content "$env:USERPROFILE\.api_key.enc" |
ConvertTo-SecureString |
ForEach-Object { [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($_)) }
Output: (no console output; secret encrypted at rest per user)
For dev workflows that must use environment variables (Node.js, Python, Docker), at least never commit a .env file and prefer dotenv-vault or similar.
Common pitfalls (continued)
setxdoes not affect the current shell — open a new cmd to see the change. The most-asked Stack Overflow question about Windows env vars is this one.setxtruncates at 1024 characters — silent truncation when extendingPATH; check withecho %PATH% | find /c /v ""or use PowerShell.set VAR =valuewith space before=— the variable is namedVAR(with a trailing space), which is unreadable from%VAR%. Use theset "VAR=value"form.- PowerShell
Set-Variableis unrelated —Set-Variablemanipulates PowerShell variables; environment variables use$env:or[Environment]. - Environment vars are not secrets — child processes inherit them; never put real secrets in
setorsetx. setwrites to current process;setxwrites to registry — they do not communicate. Run both if you want both immediate effect and persistence.%PATH%corruption on badsetx—setx PATH "%PATH%;C:\New" /Mcan pollutePATHbecause%PATH%is expanded with the current process's already-merged user+machine PATH; the resulting machine PATH ends up containing user entries. Always edit the registry value separately for user and machine.
Real-world recipes (continued)
Safe permanent PATH append (PowerShell)
The single most common environment-variable task done wrong. This recipe preserves the existing user PATH, deduplicates, and broadcasts the change.
function Add-UserPath {
param([Parameter(Mandatory)][string]$Path)
$current = [Environment]::GetEnvironmentVariable("Path","User")
$entries = $current -split ';' | Where-Object { $_ -and $_ -ne $Path }
$new = ($entries + $Path) -join ';'
[Environment]::SetEnvironmentVariable("Path", $new, "User")
Write-Host "Added to user PATH: $Path"
}
Add-UserPath -Path "C:\Tools\mingw64\bin"
Output:
Added to user PATH: C:\Tools\mingw64\bin
(Open a new shell to use it; or update $env:Path in the current session too.)
Load a .env file into the current cmd session
A small loader that parses KEY=value lines into the environment.
@echo off
for /F "usebackq tokens=1,* delims==" %%a in (".env") do (
if not "%%a"=="" if not "%%a:~0,1%"=="#" set "%%a=%%b"
)
echo Loaded vars from .env
Output:
Loaded vars from .env
PowerShell version
Get-Content .env | ForEach-Object {
if ($_ -match '^\s*([^#=][^=]*)=(.*)$') {
Set-Item "Env:$($Matches[1].Trim())" $Matches[2].Trim()
}
}
Output: (none; environment variables set from .env)
Generate a timestamped filename
%DATE% is locale-dependent — extract digits with substring slicing. The WMIC approach below is locale-proof.
@echo off
for /F "tokens=2 delims==" %%I in ('wmic os get localdatetime /value') do set DT=%%I
set STAMP=%DT:~0,8%_%DT:~8,6%
set OUTFILE=report_%STAMP%.csv
echo Output: %OUTFILE%
Output:
Output: report_20260525_142205.csv
Push and pop PATH for an isolated build
A reusable batch wrapper that prepends a tool directory, runs the build, then restores PATH cleanly.
@echo off
setlocal
set "PATH=C:\Tools\mingw64\bin;%PATH%"
set "PATH=C:\Tools\cmake\bin;%PATH%"
cmake -S . -B build
cmake --build build
endlocal
echo PATH restored automatically by endlocal.
Output:
PATH restored automatically by endlocal.
Pure-PowerShell environment snapshot
Dump current environment to JSON for archival or diffing.
Get-ChildItem Env: | Select-Object Name, Value |
ConvertTo-Json | Out-File "env_$(Get-Date -Format yyyyMMdd_HHmmss).json"
Output: (no console output; JSON file written)
# Diff two snapshots
$a = Get-Content env_20260524_120000.json | ConvertFrom-Json
$b = Get-Content env_20260525_120000.json | ConvertFrom-Json
Compare-Object $a $b -Property Name, Value
Output:
Name Value SideIndicator
---- ----- -------------
NEW_VAR added today =>
Cross-shell variable transfer (cmd → PowerShell)
Setting a variable in cmd and then reading it in a PowerShell session: spawn PowerShell from inside the same cmd so it inherits the environment.
@echo off
set BUILD_ID=2026-05-25_001
powershell -NoProfile -Command "Write-Host 'PowerShell saw BUILD_ID =' $env:BUILD_ID"
Output:
PowerShell saw BUILD_ID = 2026-05-25_001
The reverse (PowerShell setting a variable for a downstream cmd) works the same way — invoke cmd /c from inside PowerShell with $env:VAR already set.
$env:BUILD_ID = "2026-05-25_001"
cmd /c "echo cmd saw BUILD_ID=%BUILD_ID%"
Output:
cmd saw BUILD_ID=2026-05-25_001
See also
- echo — Output Text and Control Command Echo —
%VAR%expansion inechois the standard inspector forsetresults. - PowerShell Essentials —
$env:,[Environment]::SetEnvironmentVariable,Set-Variable, and parameter binding. - cls — Clear Console Screen — paired with
setin batch menus. - bcdedit — Boot Configuration Data Editor — uses
setonly for property assignment on boot entries (bcdedit /set {current} ...). - systeminfo — System Information — useful for seeing OS-level env-related info like locale and code page.
Sources
- set (environment variable) | Microsoft Learn —
setreference forcmd.exe - setx | Microsoft Learn — persistent assignment, 1024-character limit
- Environment.SetEnvironmentVariable Method | Microsoft Learn — Process / User / Machine targets used from PowerShell