Skip to content
shellmap

Set an environment variable persistently

Make an environment variable available in every NEW shell session (not just the current one) — for setting `JAVA_HOME`, adding to `PATH`, configuring API tokens for daily-use scripts.

How to set an environment variable persistently in each shell

Bashunix
echo 'export FOO=bar' >> ~/.bashrc

Per-user persistence. `~/.bashrc` for interactive non-login shells (default on most Linuxes); `~/.bash_profile` for login shells (macOS Terminal.app); some setups source one from the other. To test: `echo $-` — if it includes `i`, it's interactive; the right file depends on whether it's interactive vs login. After editing, `source ~/.bashrc` reloads in the current session, OR open a new terminal. For SYSTEM-wide (every user): `/etc/environment` (kernel-managed, no shell syntax) or `/etc/profile.d/myvar.sh` (sourced by login shells).

Zshunix
echo 'export FOO=bar' >> ~/.zshrc

macOS's default shell since macOS 10.15 (2019). `~/.zshrc` is sourced for interactive sessions; `~/.zprofile` for login sessions (macOS Terminal opens a login shell by default — set in `~/.zprofile` if you want it before any subshell). To activate in current session: `source ~/.zshrc`. For VS Code / IntelliJ / etc that may not source `.zshrc`, set the var in the GUI app's "terminal env" config too.

Fishunix
set -Ux FOO bar

`-U` (universal) is fish's special scope — persists across sessions WITHOUT writing to a rc file (stored in `~/.config/fish/fish_variables`). `-x` (export) makes it visible to subprocesses. ONE command for permanent + exported. The universal scope is fish-unique; bash/zsh have nothing equivalent (they require rc-file persistence). To remove: `set -Ue FOO`. To verify: `set -Sx FOO` shows scope+value.

PowerShellwindows
[Environment]::SetEnvironmentVariable("FOO", "bar", "User")

The third arg is scope: `"User"` (per-user, writes to `HKCU\Environment` registry), `"Machine"` (system-wide, writes to `HKLM`, requires admin), or `"Process"` (current session only, same as `$env:FOO = "bar"`). NEW pwsh sessions inherit User + Machine vars on launch — the current session does NOT see them after `SetEnvironmentVariable` (it inherited the env at startup). For session AND persistence: `$env:FOO = "bar"; [Environment]::SetEnvironmentVariable("FOO", "bar", "User")`. The pwsh-only `Update-SessionEnvironment` (Chocolatey provides this) reloads vars from the registry into the current session.

cmd.exewindows
setx FOO bar

`setx` writes to the registry (`HKCU\Environment` for user, `HKLM` with `/m` for machine). Persists across reboots and applies to NEW cmd / pwsh sessions. Does NOT affect the CURRENT cmd session (`echo %FOO%` will still be empty until you open a new window). The registry value is limited to 1024 characters — `setx PATH ...` with a long path will silently truncate. For PATH specifically, prefer GUI (`SystemPropertiesAdvanced` → Environment Variables) or pwsh `[Environment]::SetEnvironmentVariable` (no length limit).

Equivalents listed for Bash, Zsh, Fish, PowerShell, cmd.exe.

Gotchas & notes

  • The "current session vs new sessions" trap is universal. Setting a persistent var on every platform does NOT affect the current shell — only NEW shells get the value. `bash`: source the rc file (`source ~/.bashrc`). `fish`: `set -Ux` magically works in current session too (universal scope is shared). `pwsh`: set BOTH `$env:FOO` (session) AND `[Environment]::SetEnvironmentVariable(...,"User")` (persistence). `cmd`: open a new window.
  • For modifying PATH specifically (adding a directory), the correct form differs: bash `export PATH="$HOME/bin:$PATH"` (prepend), fish `set -Ux fish_user_paths $HOME/bin $fish_user_paths` (fish's special PATH variable that auto-prefixes), pwsh `$env:PATH = "$HOME\bin;$env:PATH"` (semicolon separator on Windows!), cmd `setx PATH "%PATH%;C:\bin"` (the registry-truncation trap is real for PATH — use GUI).
  • Per-application env config exists too. `direnv` (cross-platform tool, `brew install direnv` / `apt install direnv`) auto-loads a `.envrc` file per directory — perfect for project-specific API tokens that you don't want polluting your global env. Docker/Kubernetes have their own per-container env mechanisms. `dotenv`-style `.env` files are app-level (your code parses them).
  • For sensitive vars (API tokens, passwords), avoid putting them in rc files in plaintext — they end up in git accidentally, in dotfile backups, in screen-shares. Better: keychain integration (macOS `security`, gnome-keyring, Windows Credential Manager) wrapped in a function that decrypts on demand. Or use a password-manager CLI (`1password` `op run`, `bitwarden` `bw get`) to inject the value just-in-time per command.

Related commands

Related tasks