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().

cmd
set /?

Output:

css
Displays, sets, or removes cmd.exe environment variables.

SET [variable=[string]]
SET [variable]
SET /A expression
SET /P variable=[promptString]

Syntax

cmd
set [variable[=value]]
set /A expression
set /P variable=[prompt]

Output: (varies by form — see sections below)

Essential options

FormMeaning
setList all environment variables
set PREFIXList all variables whose name starts with PREFIX
set VAR=valueCreate or update VAR
set VAR=Delete VAR (empty right-hand side)
set /A VAR=exprEvaluate integer arithmetic expression and assign
set /P VAR=promptDisplay 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.

cmd
set

Output:

ini
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\alicedev\AppData\Roaming
COMPUTERNAME=MYHOST
ComSpec=C:\Windows\system32\cmd.exe
...
cmd
set path

Output:

ini
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.

cmd
set GREETING=Hello, world!
echo %GREETING%

Output:

code
Hello, world!
cmd
set PROJECT=myapp
set BASE=C:\Projects
set OUTDIR=%BASE%\%PROJECT%\dist
echo %OUTDIR%

Output:

makefile
C:\Projects\myapp\dist

Deleting a variable

Assigning an empty value removes the variable from the environment. There must be nothing after the =.

cmd
set GREETING=
set GREETING

Output:

perl
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.

cmd
set /A RESULT=7 * 6
echo %RESULT%

Output:

code
42
cmd
set COUNT=10
set /A COUNT=COUNT+1
echo %COUNT%

Output:

code
11
cmd
set /A HEX=0xFF
echo %HEX%

Output:

code
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.

cmd
set /P NAME=Enter your name: 
echo Hello, %NAME%!

Output:

yaml
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%.

cmd
set FULL=HelloWorld
echo %FULL:~0,5%

Output:

code
Hello
cmd
echo %FULL:~-5%

Output:

code
World
cmd
rem Extract year-month from %DATE% (depends on locale)
set YM=%DATE:~-4,4%-%DATE:~-10,2%
echo %YM%

Output:

yaml
2026-04

Search-and-replace expansion

%VAR:find=replace% substitutes all occurrences of find with replace in the variable's value.

cmd
set PATH_UNIX=/home/alice/projects/myapp
set PATH_WIN=%PATH_UNIX:/=\%
echo %PATH_WIN%

Output:

arduino
\home\alice\projects\myapp
cmd
rem Strip spaces from a variable
set PADDED=  hello  
set TRIMMED=%PADDED: =%
echo [%TRIMMED%]

Output:

csharp
[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.

cmd
rem Permanent user-level variable
setx MY_TOOL_HOME "C:\Tools\mytool"

Output:

makefile
SUCCESS: Specified value was saved.

Common pitfalls

  1. Trailing spaces in set VAR=value — the space before the newline is included in the value; avoid them or quote the assignment.
  2. Quotes become part of the valueset VAR="hello" stores "hello" with the quotes; drop the outer quotes for a bare string.
  3. set changes are session-scoped — closing the window discards them; use setx for persistence.
  4. /A uses integer arithmetic only — floating-point is not supported; use PowerShell or a helper script for decimals.
  5. %VAR% expansion at parse time — inside an if or for block, the value is captured when the block is parsed, not when the line runs. Use setlocal enabledelayedexpansion and !VAR! for runtime expansion.
  6. set PREFIX lists, not readsset PATH prints all variables beginning with PATH, not just %PATH%; use echo %PATH% for a single variable.

Real-world recipes

Build a timestamped output path

cmd
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:

makefile
Output: C:\Reports\report_20260428.csv

Interactive confirmation prompt in a batch script

cmd
@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:

css
Delete all temp files? [y/N]: n
Aborted.

Accumulate a counter across a loop

cmd
@echo off
setlocal enabledelayedexpansion
set COUNT=0
for %%f in (C:\Logs\*.log) do (
    set /A COUNT+=1
)
echo Found %COUNT% log files.

Output:

bash
Found 7 log files.

Temporarily override PATH for a single build

cmd
@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:

css
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.

ToolScopePersistenceTakes effect
set VAR=valueCurrent cmd.exe onlyLifetime of the shellImmediately, this shell
setx VAR valueCurrent user (default)Permanent (registry)New shells only
setx VAR value /MSystem-widePermanent (registry)New shells; requires admin
reg add HKCU\Environment ...Current userPermanent (registry)New shells; needs broadcast
[Environment]::SetEnvironmentVariable(name, value, "User")Current user (PowerShell)PermanentNew shells only
[Environment]::SetEnvironmentVariable(name, value, "Machine")System-widePermanentNew shells; needs admin
[Environment]::SetEnvironmentVariable(name, value, "Process")Current processThis process onlyImmediately

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.

cmd
setx GREETING "Hello permanent"
echo %GREETING%

Output:

makefile
SUCCESS: Specified value was saved.

(%GREETING% is still empty in this shell — open a new cmd to see it)

cmd
rem In a new cmd window
echo %GREETING%

Output:

code
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.

cmd
echo %PATH% | find /c /v ""

Output:

code
1

(Hint: the character count is in %PATH% itself; use PowerShell for precision.)

powershell
($env:PATH).Length

Output:

yaml
1872

For PATH editing, prefer PowerShell [Environment]::SetEnvironmentVariable("Path", ..., "User") which has no length cap, or edit via "System Properties → Environment Variables" GUI.

powershell
# 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 / batchPowerShell
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'
powershell
# Set a session variable
$env:GREETING = "Hello, world!"
$env:GREETING

Output:

code
Hello, world!
powershell
# Permanent user-level (writes registry; needs new shell to see)
[Environment]::SetEnvironmentVariable("GREETING", "Hello permanent", "User")

Output: (no output)

powershell
# Permanent machine-level (requires Administrator)
[Environment]::SetEnvironmentVariable("GREETING", "Hello system", "Machine")

Output: (no output; needs admin)

powershell
# Read all environment variables
Get-ChildItem Env: | Sort-Object Name

Output:

python-repl
Name                Value
----                -----
ALLUSERSPROFILE     C:\ProgramData
APPDATA             C:\Users\alicedev\AppData\Roaming
...
powershell
# 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.

TargetStorageAffectsAdmin required
"Process"Current process memory onlyThis PowerShell session onlyNo
"User"HKCU\Environment registry keyNew shells of this userNo
"Machine"HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\EnvironmentNew shells of any userYes
powershell
# 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:

powershell
# 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.

cmd
rem User-level variables
reg query HKCU\Environment

rem System-level variables
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"

Output:

css
HKEY_CURRENT_USER\Environment
    Path        REG_EXPAND_SZ    C:\Users\alicedev\AppData\Local\Programs\Python\Python313;...
    GREETING    REG_SZ           Hello permanent
    ...
cmd
rem Read a single user var
reg query HKCU\Environment /v GREETING

Output:

markdown
HKEY_CURRENT_USER\Environment
    GREETING    REG_SZ    Hello permanent
cmd
rem Write directly to the registry (does not broadcast WM_SETTINGCHANGE)
reg add HKCU\Environment /v FOO /t REG_SZ /d "bar" /f

Output:

code
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.

VariableValue
%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
cmd
echo Current dir: %CD%
echo Time now:    %TIME%
echo Random:      %RANDOM%

Output:

yaml
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:

cmd
set TMP_FILE=C:\Temp\work_%RANDOM%_%RANDOM%.tmp
echo Using %TMP_FILE%

Output:

sql
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.

cmd
@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:

csharp
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%:

cmd
@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:

csharp
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:

cmd
rem Wrong — quotes are part of the value
set NAME="Alice"
echo "[%NAME%]"

Output:

css
["Alice"]
cmd
rem Right — the entire assignment is quoted
set "NAME=Alice"
echo [%NAME%]

Output:

csharp
[Alice]
cmd
rem Trailing spaces preserved
set "PROMPT_TEXT=Enter: "
set /P RESPONSE=%PROMPT_TEXT%

Output:

makefile
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.

cmd
@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:

ini
Inside: OUTSIDE=changed, INSIDE=inner
Outside: OUTSIDE=before, INSIDE=[]

To leak a single value out of a setlocal block, use the for /F trick:

cmd
@echo off
setlocal
set COMPUTED=hello-%RANDOM%
(endlocal & set RESULT=%COMPUTED%)
echo Result outside: %RESULT%

Output:

rust
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:

cmd
@echo off
setlocal enableextensions
if errorlevel 1 (
    echo Extensions required — exiting.
    exit /b 1
)
set /A SUM=2+2
echo %SUM%

Output:

code
4

To enable both extensions and delayed expansion in one line:

cmd
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.

OperatorMeaningExample
+ - * /Arithmeticset /A R=10/3 → 3
%Moduloset /A R=10%%3 → 1 (in cmd, escape % as %%)
( )Groupingset /A R=(2+3)*4 → 20
<< >>Bit shiftsset /A R=1<<4 → 16
&Bitwise ANDset /A R=12&10 → 8
``Bitwise OR
^Bitwise XORset /A R=12^^10 → 6 (escape ^)
~Bitwise NOTset /A R=~0 → -1
!Logical NOTset /A R=!0 → 1
,Statement separatorset /A A=1, B=2, R=A+B
= += -= *= /= %= &= `= ^= <<= >>=`Compound assignment
0x prefixHexadecimal literalset /A R=0xFF → 255
0 prefixOctal literalset /A R=010 → 8
cmd
set /A R=(100 + 50) / 5
echo %R%

Output:

code
30
cmd
set /A FLAGS=0x01 ^| 0x04
echo %FLAGS%

Output:

code
5

Substring and replacement modifier reference

FormEffect
%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
cmd
set S=HelloWorld

echo %S:~0,5%
echo %S:~5%
echo %S:~-5%
echo %S:~2,3%
echo %S:World=Friend%

Output:

code
Hello
World
World
llo
HelloFriend

For dynamically computed start/length, use a helper variable:

cmd
set S=abcdefghij
set /A POS=3
set /A LEN=4
call set CHUNK=%%S:~%POS%,%LEN%%%
echo %CHUNK%

Output:

code
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).

ModifierMeaning
%~1Remove surrounding quotes from %1
%~f1Full path
%~d1Drive letter
%~p1Path (without drive)
%~n1File name
%~x1Extension (with leading dot)
%~s1Short (8.3) path
%~a1File attributes
%~t1Last-modified timestamp
%~z1File size in bytes
%~dp1Drive + path (most useful for "directory of the script")
%~nx1Name + extension
%~ftza1Combine modifiers
cmd
@echo off
echo Script: %~nx0
echo Dir:    %~dp0
echo First arg full path: %~f1

Output (if invoked as myscript.bat C:\file.txt):

yaml
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:

cmd
@echo off
set SCRIPT_DIR=%~dp0
echo Config at: %SCRIPT_DIR%config.ini

Output:

arduino
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.
powershell
# 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)

  1. setx does 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.
  2. setx truncates at 1024 characters — silent truncation when extending PATH; check with echo %PATH% | find /c /v "" or use PowerShell.
  3. set VAR =value with space before = — the variable is named VAR (with a trailing space), which is unreadable from %VAR%. Use the set "VAR=value" form.
  4. PowerShell Set-Variable is unrelatedSet-Variable manipulates PowerShell variables; environment variables use $env: or [Environment].
  5. Environment vars are not secrets — child processes inherit them; never put real secrets in set or setx.
  6. set writes to current process; setx writes to registry — they do not communicate. Run both if you want both immediate effect and persistence.
  7. %PATH% corruption on bad setxsetx PATH "%PATH%;C:\New" /M can pollute PATH because %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.

powershell
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:

css
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.

cmd
@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:

python
Loaded vars from .env

PowerShell version

powershell
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.

cmd
@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:

makefile
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.

cmd
@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:

csharp
PATH restored automatically by endlocal.

Pure-PowerShell environment snapshot

Dump current environment to JSON for archival or diffing.

powershell
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)

powershell
# 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:

ini
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.

cmd
@echo off
set BUILD_ID=2026-05-25_001
powershell -NoProfile -Command "Write-Host 'PowerShell saw BUILD_ID =' $env:BUILD_ID"

Output:

ini
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.

powershell
$env:BUILD_ID = "2026-05-25_001"
cmd /c "echo cmd saw BUILD_ID=%BUILD_ID%"

Output:

ini
cmd saw BUILD_ID=2026-05-25_001

See also

Sources