Skip to content
shellmap

basenameStrip directory part from a path, keeping only the filename across all 5 shells

Equivalents in every shell

Bashunix
basename /var/log/syslog

Prints `syslog`. Optional 2nd arg strips a suffix: `basename foo.txt .txt` → `foo`. POSIX-mandated, present on every Unix.

Zshunix
basename /var/log/syslog

Same external `basename` binary. Zsh also has the `:t` parameter modifier as a builtin: `${path:t}` returns the tail without forking — faster in tight loops.

Fishunix
basename /var/log/syslog

Same external binary. Fish exposes `path basename /var/log/syslog` as a builtin (fish 3.5+) that handles multiple paths and an explicit `--extension` flag for suffix stripping.

PowerShellwindows
Split-Path -Leaf "C:\Windows\System32\drivers\etc\hosts"

`-Leaf` returns the last component. `[System.IO.Path]::GetFileName(...)` is the .NET equivalent. To strip the extension as well, chain `-LeafBase` (pwsh 6+) or use `[IO.Path]::GetFileNameWithoutExtension(...)`.

cmd.exewindows
for %I in ("C:\Windows\System32\drivers\etc\hosts") do @echo %~nxI

No native `basename`. The `%~nx` variable modifier on a `for` loop variable returns name+extension. Use `%~n` for name-only (no extension), `%~x` for extension-only.

Worked examples

Get the filename portion of a path

Bash
basename /usr/local/bin/node
Fish
basename /usr/local/bin/node
PowerShell
Split-Path -Leaf "C:\Program Files\nodejs\node.exe"
cmd.exe
for %I in ("C:\Program Files\nodejs\node.exe") do @echo %~nxI

Strip both directory AND a known suffix (e.g. `.tar.gz` → bare name)

Bash
basename release-2026.tar.gz .tar.gz
Fish
path basename --extension .tar.gz release-2026.tar.gz
PowerShell
[IO.Path]::GetFileNameWithoutExtension("release-2026.tar.gz") -replace "\.tar$",""
cmd.exe
for %I in ("release-2026.tar.gz") do @echo %~nI

Batch-rename: for each `.bak` file, print its base without suffix

Bash
for f in *.bak; do basename "$f" .bak; done
Fish
for f in *.bak; path basename --extension .bak $f; end
PowerShell
Get-ChildItem *.bak | ForEach-Object { $_.BaseName }
cmd.exe
for %I in (*.bak) do @echo %~nI

Gotchas

  • Trailing slashes are stripped: `basename /var/log/` → `log`, not the empty string. This contradicts intuition for "leaf component" — if you need empty-on-dir-slash semantics, check the string explicitly first.
  • POSIX `basename` only takes ONE path at a time. GNU coreutils 8.16+ added `-a` for multiple args (`basename -a /a/b /c/d` → two lines), but BusyBox/Alpine and macOS BSD versions don't support it — scripts that must run on Alpine should loop instead of relying on `-a`.
  • `basename` with a suffix arg is a string-suffix match, NOT extension-aware. `basename foo.txt .txt` works, but `basename foo.TXT .txt` returns `foo.TXT` (case-sensitive). Use `basename "${f,,}"` (bash lowercasing) or normalise filenames first.
  • PowerShell `Split-Path` on a forward-slash path on Windows mostly works (the API normalises) but mixed `\` and `/` in the same string can confuse `Split-Path -Leaf` — prefer `[IO.Path]::GetFileName(...)` for cross-OS robustness in pwsh 7+.
  • cmd `%~nxI` works only inside a `for /f` or `for %I` loop — there's no direct expansion outside a loop. Workarounds use a transient single-iteration `for ("path")` like the example above, which is the idiomatic cmd trick.

WSL & PowerShell Core notes

pwshpwsh 6+ exposes `$path | Split-Path -Leaf` AND `(Get-Item $path).Name` AND `[IO.Path]::GetFileName($path)` — pick `Split-Path -Leaf` for pipeline-friendliness, `Get-Item .Name` if you already have a `FileInfo`/`DirectoryInfo` object. `BaseName` (pwsh 6+) is the suffix-stripped form. All three behave identically across Windows / Linux / macOS pwsh.
WSLWSL `basename` is GNU coreutils — identical to native Linux. Paths that include `/mnt/c/...` are treated as plain strings; `basename /mnt/c/Users/maggie/file.txt` returns `file.txt` correctly. To convert between WSL paths and Windows paths first, use `wslpath -w` / `wslpath -u`.

Related commands