Skip to content
shellmap

printenvPrint the value of an environment variable (or all of them) across all 5 shells

Equivalents in every shell

Bashunix
printenv PATH

External binary from coreutils. `printenv` (no args) lists every env var like `env`; `printenv NAME` prints only `NAME`'s value (no `NAME=` prefix), exits 0 if set or 1 if unset. Multi-name form: `printenv USER HOME SHELL` prints each on its own line. Equivalent to `echo "$NAME"` for set vars BUT exits non-zero when unset (useful for scripts: `printenv DEBUG || exit 1`).

Zshunix
printenv PATH

Same coreutils binary. Zsh-specific alternative: `print -r -- $NAME` (the `-r` is "raw", `--` ends option parsing). `print` is faster than `printenv` (builtin vs fork), at the cost of NOT distinguishing set-empty (`NAME=""`) from unset — both print an empty line. Use `printenv` when that distinction matters; `print -r --` for hot loops.

Fishunix
printenv PATH

Same external. Fish's native alternative for env vars is `echo $NAME` (fish auto-splits on whitespace less aggressively than bash, so `echo $PATH` shows the path as one line of colon-separated values, not multi-word-split). For "test if env var is set", fish uses `set -q -x NAME; and printenv NAME` — `set -q -x` checks if it's set AND exported.

PowerShellwindows
$env:PATH

The cleanest way — `$env:NAME` reads any env var as a string; `$null` if unset. For the exits-non-zero-when-unset semantic of `printenv NAME`, wrap: `if (-not $env:FOO) { exit 1 } else { Write-Output $env:FOO }`. For an enumeration with name AND value: `Get-ChildItem Env: | Where-Object Name -eq "FOO"`. To read a specific var via .NET: `[Environment]::GetEnvironmentVariable("FOO")` — same result but explicit-scope variants `("FOO", "User")` / `("FOO", "Machine")` read directly from the registry, bypassing the current-process env (handy when debugging "I set it with setx but it's not showing up").

cmd.exewindows
echo %PATH%

cmd-style env expansion. `echo %PATH%` prints the value; if `PATH` is unset, prints the literal string `%PATH%` (no error — different from bash where `$UNSET` becomes empty). The closer printenv-style behaviour: `set NAME` — lists every var starting with `NAME` (i.e., `set FOO` shows `FOO=bar` if set, nothing if unset; check `errorlevel` to detect). For "echo if set, else fail" semantics: `set FOO >nul 2>&1 && echo %FOO% || exit /b 1`.

Worked examples

Print the value of $HOME / $USERPROFILE

Bash
printenv HOME
Zsh
printenv HOME
Fish
printenv HOME
PowerShell
$env:USERPROFILE
cmd.exe
echo %USERPROFILE%

Test "is this env var set" in a script

Bash
printenv DEBUG || { echo "DEBUG is unset"; exit 1; }
Fish
set -q -x DEBUG; or begin; echo "DEBUG is unset"; exit 1; end
PowerShell
if (-not $env:DEBUG) { Write-Error "DEBUG is unset"; exit 1 }
cmd.exe
if not defined DEBUG (echo DEBUG is unset & exit /b 1)

Print multiple vars at once

Bash
printenv USER HOME SHELL
PowerShell
"$env:USERNAME","$env:USERPROFILE","$env:COMSPEC"
cmd.exe
echo %USERNAME% & echo %USERPROFILE% & echo %COMSPEC%

Gotchas

  • `printenv` exits non-zero (typically 1) when the named var is UNSET, but exits 0 when set-to-empty (`FOO=""`). Scripts that conflate "unset" and "empty" miss the distinction. Bash builtin `[ -z "${FOO+x}" ]` is the canonical test for "completely unset, not just empty". The pwsh equivalent: `[Environment]::GetEnvironmentVariable("FOO") -eq $null` (true only for unset; `""` returns the empty string).
  • cmd `echo %UNSET_VAR%` prints the LITERAL string `%UNSET_VAR%` (no error, no empty). Bash `echo $UNSET_VAR` prints empty. Script-portability trap: a cmd script ported to bash will silently silence variables; a bash script ported to cmd will print literal `$VAR` token strings into output (and downstream consumers like CSV / JSON parsers will choke).
  • cmd `if defined NAME` is the correct existence test (works for set-to-empty too: `set NAME=` then `if defined NAME` is FALSE because empty-set unsets in cmd semantics). Don't use `if "%NAME%"==""` — that returns true for unset, set-to-empty, AND set-to-literal-empty-string, conflating three different states.
  • pwsh `$env:NAME` is type-narrowed to `[string]` always — never `$null` if set, never `[int]`. To pass an env-derived number to a numeric API, explicit-cast: `[int]$env:PORT`. Without the cast, `5 + $env:PORT` does STRING concatenation (`"58000"`), not addition (`8005`).
  • Bash `${NAME:?error message}` is a one-line "require this env var or die" primitive — exits non-zero AND prints the error to stderr if unset OR empty. The pwsh equivalent needs explicit logic: `if (-not $env:NAME) { throw "NAME is required" }`. Use the bash form in CI scripts where missing config should kill the build immediately.

WSL & PowerShell Core notes

pwsh`$env:NAME` is the canonical pwsh idiom on every OS. The .NET API `[Environment]::GetEnvironmentVariable($name, $scope)` is more powerful — the optional `$scope` (`Process`, `User`, `Machine`) controls WHICH env layer to read from. After `setx FOO bar` on Windows, the current pwsh process won't see `FOO` (it inherited the env at launch), but `[Environment]::GetEnvironmentVariable("FOO", "User")` reads the registry directly and finds it. Useful for "did setx work" debugging.
WSLWSL bash `printenv WINDOWSPATH` won't see Windows-side env vars unless `WSLENV` was configured to forward them. Conversely, Windows `cmd /c echo %LINUX_VAR%` from inside WSL won't see WSL-side env vars without similar forwarding. For one-shot inspection of "what does the Windows side see for var X", `cmd.exe /c "echo %X%"` (note the executable suffix) runs in the Windows env. The `wslpath` utility converts paths between conventions when forwarding vars.

Common tasks using printenv

Related commands