Skip to content
shellmap

realpathResolve a path to its absolute, canonical form across all 5 shells

Equivalents in every shell

Bashunix
realpath ./symlink

External (GNU coreutils on Linux; macOS via `brew install coreutils` provides `grealpath`). Resolves all symlinks and `..` segments to produce a canonical absolute path. `-s` / `--no-symlinks` skips symlink resolution; `-m` allows the target to be missing.

Zshunix
realpath ./symlink

Same external binary. Zsh's `${param:A}` parameter expansion does the same in pure shell: `print -r ${path:A}` canonicalises without spawning a process. Use `:P` (zsh 5.3+) for safer "physical path" semantics.

Fishunix
realpath ./symlink

External binary. Fish 3.6+ also ships `path resolve`: `path resolve ./symlink` does the same canonicalisation without launching `realpath` — faster for tight loops.

PowerShellwindows
Resolve-Path ./symlink

Returns a `PathInfo` whose `.Path` is the absolute, cwd-resolved path. For symlink target resolution use `(Get-Item ./symlink).Target` (Windows 10 1607+). `[System.IO.Path]::GetFullPath($p)` works even on paths that do not yet exist.

cmd.exewindows
for %i in (".\file") do @echo %~fi

No `realpath`. The `%~f` modifier inside a `for` loop expands to the full path of a parameter. Symlinks are NOT resolved — use PowerShell's `Resolve-Path` or `(Get-Item ...).Target` for that.

Worked examples

Print the absolute path of a relative reference

Bash
realpath ./script.sh
Zsh
print -r ${./script.sh:A}
Fish
realpath ./script.sh
PowerShell
(Resolve-Path ./script.sh).Path
cmd.exe
for %i in (.\script.sh) do @echo %~fi

Resolve a symlink to its target

Bash
realpath /usr/bin/python3
Fish
path resolve /usr/bin/python3
PowerShell
(Get-Item /usr/bin/python3).Target

Get the directory containing the running script

Bash
SCRIPT_DIR=$(dirname "$(realpath "$0")")
Zsh
SCRIPT_DIR=${0:A:h}
PowerShell
$SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Path

Gotchas

  • macOS does NOT ship `realpath` in the base system on older versions — it arrived with newer BSD coreutils but may be missing on legacy installs. Scripts must either depend on `brew install coreutils` + `grealpath`, or use a portable fallback: `python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$path"`.
  • `realpath` REQUIRES the target to exist by default. Use `realpath -m` (or `--canonicalize-missing`) to canonicalise a path that does NOT yet exist — useful when computing "where will this file be written" without first creating it.
  • Zsh `${path:A}` (capital A) resolves symlinks; `${path:a}` (lowercase) does NOT — it only collapses `.` and `..` lexically. The one-character difference is a frequent bug source. `:P` (zsh 5.3+) is a safer alias for physical-path resolution.
  • PowerShell `Resolve-Path` THROWS if the path does not exist. Use `[System.IO.Path]::GetFullPath($p)` (works on missing paths) when you only need string canonicalisation, not symlink resolution. The two methods produce different results on real symlinks — pick deliberately.
  • `realpath` collapses `..` AFTER resolving symlinks, so the result may NOT contain the literal path segments you wrote. `cd /tmp; realpath link/../other` resolves `link` first then strips the `..` — easy to misread as `/tmp/other` when the actual result depends on the link target.

WSL & PowerShell Core notes

pwsh`Resolve-Path` and `(Get-Item ...).Target` work identically on Windows, Linux, and macOS pwsh — but Windows uses backslash separators by default while Linux/macOS use forward slashes. Normalise with `[System.IO.Path]::DirectorySeparatorChar` if a script must compare paths cross-platform.
WSLWSL `realpath /mnt/c/Users/me` returns a Linux-style path. To convert to a Windows path, pipe through `wslpath -w` (e.g. `wslpath -w $(realpath /mnt/c/file)` → `C:\file`). The reverse direction (`wslpath -u`) goes Windows → Linux — essential for any script crossing the boundary.

Related commands