Linux basename Command: Get File Name from Path in Bash

By 

Updated on

5 min read

Using the Linux basename command to strip directory paths from file names

When a script loops over a list of full paths, you usually need only the last part of each one: the file name, often without its extension. The basename command does exactly that. It strips the leading directory path and an optional trailing suffix, then prints what remains.

This guide explains how to use basename on the command line and inside Bash scripts, with practical examples.

Syntax

basename supports two syntax forms:

txt
basename NAME [SUFFIX]
basename OPTION... NAME...

The first form takes a single name and an optional suffix to strip. The second form, used with -a, accepts multiple names at once.

Basic Usage

The most common use is to strip the directory path from a full file path:

Terminal
basename /etc/passwd
output
passwd

basename also removes any trailing / characters, so both of the following produce the same result:

Terminal
basename /usr/local/
basename /usr/local
output
local
local

Multiple Inputs

Use the -a (--multiple) option to process several paths at once, separated by spaces:

Terminal
basename -a /etc/passwd /etc/shadow
output
passwd
shadow

This is equivalent to running basename separately on each path and collecting the output.

Remove a Trailing Suffix

Pass the suffix as a second argument to strip it from the result:

Terminal
basename /etc/sysctl.conf .conf
output
sysctl

The same result can be achieved with the -s (--suffix=SUFFIX) option:

Terminal
basename -s .conf /etc/sysctl.conf
output
sysctl

Because -s already implies -a, it is the form to reach for when stripping a suffix from several names at once:

Terminal
basename -a -s .conf /etc/sysctl.conf /etc/sudo.conf
output
sysctl
sudo

NUL-Terminated Output

By default, each result ends with a newline. Use the -z (--zero) option to end lines with a NUL character instead of a newline. This is useful when passing output to tools that handle NUL-delimited input, such as xargs -0:

Terminal
basename -az -s .conf /etc/sysctl.conf /etc/sudo.conf | xargs -0 echo
output
sysctl sudo

Using basename in a Bash Script

Inside a script, the usual pattern is to capture the result with command substitution and keep it in a variable:

Terminal
path="/var/log/nginx/access.log"
file="$(basename "$path")"
echo "$file"
output
access.log

Keep "$path" quoted so a path containing spaces remains a single argument. Without those quotes, Bash splits the path into separate operands, so basename may produce the wrong result or exit with an error.

Pass the suffix as a second argument to drop the extension at the same time:

Terminal
file="$(basename "$path" .log)"
echo "$file"
output
access

One edge case is worth guarding against. If the name begins with a dash, basename reads it as an option and exits with an error instead of printing anything. Separate the options from the operands with -- whenever the path comes from user input or from another command:

Terminal
name="$(basename -- "$user_input")"

Parameter Expansion Instead of basename

Bash can do the same job on its own, without starting a new process. Parameter expansion with ##*/ removes everything up to and including the last slash:

Terminal
path="/var/log/nginx/access.log"
echo "${path##*/}"
output
access.log

Applying %.* to that result strips the extension:

Terminal
file="${path##*/}"
echo "${file%.*}"
output
access

The two approaches are not quite interchangeable. Every basename call forks a subprocess, so parameter expansion is measurably faster in a loop over thousands of files. On the other hand, basename handles trailing slashes for you: basename /usr/local/ prints local, while ${path##*/} on the same value expands to an empty string.

Rename Files in a Script

The following example shows how to use basename inside a Bash for loop to rename files in the current directory, replacing the .jpeg extension with .jpg. It uses mv -n, so an existing .jpg file is not overwritten:

sh
for file in ./*.jpeg; do
    [ -e "$file" ] || continue
    mv -n -- "$file" "$(basename -- "$file" .jpeg).jpg"
done

Starting the glob with ./ keeps names beginning with a dash from being parsed as options, while the existence check skips the loop when no .jpeg files match.

For bulk file renaming with more advanced pattern matching, the rename command is often a more concise alternative.

Quick Reference

For a printable quick reference, see the Linux commands cheatsheet .

TaskCommand
Strip directory from pathbasename /path/to/file
Strip directory and suffixbasename /path/to/file.txt .txt
Strip suffix with -sbasename -s .txt /path/to/file.txt
Process multiple pathsbasename -a /path/one /path/two
Strip suffix from multiple pathsbasename -a -s .conf /etc/a.conf /etc/b.conf
NUL-terminated outputbasename -z /path/to/file
Capture in a script variablefile="$(basename "$path")"
Strip directory in Bash (no trailing slash)file="${path##*/}"

FAQ

What is the difference between basename and dirname?
basename prints the last component of a path (the file or directory name), while dirname prints the directory portion. They are complementary tools, but their output may normalize repeated or trailing slashes.

Does basename modify any files?
No. basename only parses the string you pass to it and prints the result. It does not read from or write to the filesystem.

Can basename strip any suffix, not just file extensions?
Yes. The suffix is a plain string match against the end of the name. basename report_final.txt _final.txt produces report. The suffix must appear at the very end of the name to be removed.

How do I strip a file extension in a Bash script without calling basename?
Use parameter expansion: ${filename%.*} removes the shortest trailing suffix matching .*, and ${path##*/} drops the directory part. See Parameter Expansion Instead of basename for the trade-offs.

Conclusion

The basename command strips the directory path and optional suffix from a file name, which makes it the quickest way to pull a file name out of a full path in a shell script. When the same script runs over very large file lists, reach for ${path##*/} instead and save a subprocess on every iteration.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page