Skip to content
shellmap

Detect the current shell

Determine which shell (bash / zsh / fish / pwsh / cmd) is currently running — for conditional dotfile loading and script-detection scenarios.

How to detect the current shell in each shell

Bashunix
ps -p $$ -o comm=

`$$` = current shell's PID. `ps -o comm=` prints just the command name (`bash`, `zsh`, etc.). `$SHELL` is the USER'S DEFAULT shell from `/etc/passwd` — NOT necessarily the one currently running (you can `bash -c 'echo $SHELL'` from a zsh session and get `zsh`).

Zshunix
ps -p $$ -o comm=
Fishunix
ps -p $fish_pid -o comm=

Fish exposes `$fish_pid` (not `$$` — fish has no `$$` POSIX variable). For "am I in fish?" check `$FISH_VERSION` is set: `if set -q FISH_VERSION; ...; end`.

PowerShellwindows
$PSVersionTable.PSEdition

`Desktop` = Windows PowerShell 5.1 (legacy); `Core` = pwsh 6+ (cross-platform). Full version: `$PSVersionTable.PSVersion`. For "am I in pwsh at all": `$PSVersionTable` is `$null` outside pwsh (so checking its existence works as a probe).

cmd.exewindows
echo %COMSPEC%

`%COMSPEC%` is `C:\Windows\System32\cmd.exe` — the path to the current shell. Set automatically by Windows. For pwsh-vs-cmd detection inside a `.bat` file, test for `%PSModulePath%` (pwsh sets it, cmd doesn't — but Windows 11 sets it system-wide so this is fragile; better to test `%PSExecutionPolicyPreference%`).

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

Gotchas & notes

  • **`$SHELL` is the WRONG variable to check** — it's the shell listed in `/etc/passwd` for the current user (set by `chsh`), NOT the shell currently executing. Inside `bash -c "echo $SHELL"` from a zsh session, `$SHELL` is still `/usr/bin/zsh`. The CORRECT idiom is `ps -p $$ -o comm=` (`$$` = current PID, `comm=` strips header). Or check shell-specific magic vars: `$BASH_VERSION` set → bash; `$ZSH_VERSION` set → zsh; `$FISH_VERSION` set → fish; `$PSVersionTable` defined → pwsh. None set → POSIX sh or dash.
  • **Cross-shell script idiom**: at the top of a dotfile, you often need different setup per shell. The reliable check: `if [ -n "$BASH_VERSION" ]; then ... fi`, `if [ -n "$ZSH_VERSION" ]; then ... fi`. Inside fish: `if set -q FISH_VERSION` (different syntax — `set -q` tests existence). NEVER use `$SHELL`. Some tools (rbenv, pyenv) sniff the shell via `$SHELL` for init code generation — they're wrong but conventional, so `rbenv init -` returns the `$SHELL`-appropriate init regardless of the actually-running shell.
  • pwsh detection layers: (1) pwsh exists at all → `$PSVersionTable` is defined; (2) which edition → `$PSVersionTable.PSEdition` is `Desktop` (5.1) or `Core` (6+); (3) which OS → `$IsWindows` / `$IsLinux` / `$IsMacOS` (pwsh 6+ only — 5.1 doesn't have these); (4) which platform → `$PSVersionTable.Platform` (`Win32NT` / `Unix`). For "is this pwsh 7+": `if ($PSVersionTable.PSVersion.Major -ge 7) { ... }`. The 5.1 vs 7 difference is huge: 5.1 default encoding is locale-dependent (often Windows-1252); 7 default is UTF-8-no-BOM. Many cmdlets behave differently across the two; explicit version-gating in scripts is best practice.
  • cmd vs pwsh detection from a `.bat` file (rare but real, for installer scripts): `%COMSPEC%` is set in BOTH cmd (`cmd.exe`) and pwsh-invoked-cmd (still `cmd.exe`). Better probe: `if defined PSModulePath` works on Windows 11+ where pwsh sets it; less reliable on Win10. Most-robust: `where powershell.exe >nul 2>&1 && set "have_ps=1"` to detect availability rather than current-shell. For a `.ps1` file, you're definitely in pwsh — `if ($PSScriptRoot)` is non-empty during script execution; `if ($MyInvocation.MyCommand.Path)` returns the script path. Don't try to write polyglot cmd/pwsh files — the syntax for `if` and variables is too divergent.

Related commands

Related tasks