Skip to content
shellmap

test-pathCheck whether a path exists — bash [ -e ] equivalent across all 5 shells

Equivalents in every shell

Bashunix
[ -e /etc/hosts ]

`[ -e path ]` is "exists, of any type"; `-f` is "regular file"; `-d` is "directory"; `-L` is "symbolic link" (does not follow); `-r`/`-w`/`-x` are read/write/execute permission checks. The bracketed form is a builtin (POSIX `test`), invoked from `if` / `&&` / `||`. `[[ -e path ]]` (bash/zsh extended test) is similar with no word-splitting.

Zshunix
[[ -e /etc/hosts ]]

Same POSIX-flag set on `[[ ]]` (bash/zsh extended test) plus zsh-only: `-N` "modified after access" (a less-known classic). `[[ ]]` does not split words / glob, so quoting is forgiving compared to `[ ]`. zsh also exposes `(.)` (regular files only), `(/)` (directories only) glob qualifiers — `print -l *(.)` is "all regular files in cwd" without an explicit test.

Fishunix
test -e /etc/hosts

`test` is fish-builtin (NOT the external `/usr/bin/test`) and supports the same POSIX flags. There is no `[[ ]]` in fish. Combine via fish boolean operators: `test -e $f; and test -r $f`. Fish does NOT do bash-style short-circuit (`&&` / `||`) — use `and` / `or` keywords.

PowerShellwindows
Test-Path /etc/hosts

PowerShell-native cmdlet. Returns `$true`/`$false` (a real bool, not an exit code). `-PathType Container` checks "is it a directory"; `-PathType Leaf` is "regular file". `-NewerThan` and `-OlderThan` accept DateTime for time-based existence tests. Works across PS providers — `Test-Path HKLM:\SOFTWARE\MyApp` checks a registry key.

cmd.exewindows
if exist C:\Windows\System32\notepad.exe (echo yes)

`if exist <path>` is the only built-in. Trailing `\` (`if exist C:\Windows\`) tests for a directory — the only way to differentiate file-vs-directory in cmd. There is no permission test; `dir <path>` and a swallowed errorlevel is the workaround for "is it readable". Wildcards work: `if exist *.log (echo found)`.

Worked examples

Check if a file exists

Bash
[ -f /etc/hosts ] && echo found
Zsh
[[ -f /etc/hosts ]] && echo found
Fish
test -f /etc/hosts; and echo found
PowerShell
if (Test-Path /etc/hosts -PathType Leaf) { "found" }
cmd.exe
if exist C:\Windows\notepad.exe echo found

Check if a directory exists

Bash
[ -d ~/.config ] && echo dir
PowerShell
if (Test-Path ~/.config -PathType Container) { "dir" }
cmd.exe
if exist C:\Users\me\ echo dir

Check if any file matches a glob

Bash
compgen -G "*.log" > /dev/null && echo "logs present"
PowerShell
if (Test-Path "*.log") { "logs present" }
cmd.exe
if exist *.log echo logs present

Gotchas

  • `Test-Path path` returns `$true` for BOTH files AND directories by default — explicit `-PathType Leaf` or `-PathType Container` is required to distinguish them. The bash `[ -e path ]` has the same any-type semantics, but `[ -f ]` (file only) and `[ -d ]` (dir only) are short and habitual, so people forget to add `-PathType` to PS code. Caused production bugs where `Test-Path config.json` was true for a *directory* called `config.json`.
  • PowerShell wildcards on `Test-Path` (`Test-Path *.log`) return `$true` if ANY match exists — convenient. But `Test-Path '['` (a literal `[` character) FAILS because `[` is a wildcard metacharacter. Use `-LiteralPath` to disable wildcard interpretation: `Test-Path -LiteralPath '[brackets].txt'`. Same gotcha applies to filenames with `]`, `*`, `?`.
  • `[ -e /broken/symlink ]` is FALSE if the symlink target does not exist (it follows the link). `[ -L /broken/symlink ]` is TRUE (does not follow). Production scripts that clean up dangling symlinks must use `-L`, not `-e` — the bug is `-e` quietly skipping the broken link instead of removing it.
  • fish `test -e $var` with an UNSET variable gives an error (`test: Missing argument`), not false — fish does not auto-promote unset to empty string the way bash does. Quote the variable to force empty-string-on-unset: `test -e "$var"`. POSIX `[ -e "$var" ]` (with the quotes) handles this; without quotes, both shells word-split.
  • cmd `if exist` evaluates trailing-`\` as the directory check — `if exist C:\Windows\System32` is true even though System32 is a DIRECTORY (no trailing slash treats as 'name exists'). To force file-only check, `if exist <path> ( if not exist <path>\NUL ( echo it is a file ))` — the `\NUL` device trick is the only built-in file-vs-dir distinction. PowerShell's `-PathType Leaf` is dramatically cleaner.

WSL & PowerShell Core notes

pwsh`Test-Path` is fully cross-platform on pwsh. It honours OS-native path separators (`/` on Linux/macOS, `\` on Windows, both accepted on either) and supports the same `-PathType` / `-LiteralPath` / `-NewerThan` flags. Cross-platform scripts can rely on it without platform branching for the file-system provider; for `HKLM:\` / `HKCU:\` it works on Windows only.
WSLFrom WSL bash, the Linux `test` builtin checks Linux paths. From Windows pwsh, `Test-Path \\wsl$\Ubuntu\home\me\file` reaches into a running WSL distro's file system — but only if the distro is currently running (otherwise returns false silently). A common gotcha: `Test-Path` returns false for a stopped WSL distro's files, NOT an error.

Related commands