Strip Paths and Suffixes with basename
A build script hands you /srv/app/releases/v4.2.tar.gz and you just want v4.2 for a log line: that is what basename is for. This guide gets you a small, reliable pattern for turning a path into a filename, dropping a known suffix, and feeding several results to another command. The examples use GNU basename 9.4 from the Ubuntu coreutils package installed here as version 9.4-3ubuntu6.3.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and the ordinary basename command. Nothing here needs elevated privileges, and the examples only read names and write output to the terminal. The command does not rename, copy or delete files.
1. Confirm the installed command
Check the binary and package before relying on a feature in a script:
$ command -v basename
/usr/bin/basename
$ basename --version | head -1
basename (GNU coreutils) 9.4
$ dpkg-query -W -f='\${Package} \${Version}\n' coreutils
coreutils 9.4-3ubuntu6.3
Your package revision may differ even when the upstream coreutils version is the same. The installed manual page is the contract used for the rest of this guide. If you are on another Unix, read its local help with basename --help instead, because option sets and details can vary.
2. Strip leading directories
Pass one name to get the final component. This is text processing only: the path does not have to exist.
$ basename /srv/app/releases/v4.2.tar.gz
v4.2.tar.gz
$ basename ./reports/weekly.txt
weekly.txt
- No filesystem check.
basenameremoves leading directory components and leaves the rest unchanged; a missing path produces a result just like an existing one. - Good for labels. That makes it handy for log messages and generated destination names, but a successful result is never proof that a file exists.
Checkpoint
If your script needs to act on the file rather than just display its name, test that separately with an explicit file check:
path='/srv/app/releases/v4.2.tar.gz'
if [ -e "$path" ]; then
printf 'found: %s\n' "$(basename "$path")"
else
printf 'missing: %s\n' "$path" >&2
exit 1
fi
Keep the path in a quoted variable. Quoting stops spaces, wildcards and shell metacharacters in the input becoming extra shell syntax.
3. Remove one trailing suffix
Give a second argument when the filename has a known suffix:
$ basename include/stdio.h .h
stdio
$ basename notes.txt .md
notes.txt
The suffix is removed only when it sits at the end of the name, after directory components have already been stripped. It is not a pattern: .md does not touch the similar-looking part of notes.txt.
For a variable suffix, quote both arguments:
path='/srv/app/releases/v4.2.tar.gz'
suffix='.tar.gz'
stem=$(basename "$path" "$suffix")
printf 'stem: %s\n' "$stem"
# stem: v4.2
Command substitution strips trailing newlines from the output, which is exactly what you want for a filename component. Do not use this form for arbitrary records that may contain embedded newlines; use a NUL-delimited pipeline instead, covered in step 5.
4. Process several names deliberately
The plain one-name form is easy to misread once more arguments are involved. Use -a or --multiple to say that every remaining argument is a name:
$ basename --multiple /srv/app/one.log /srv/app/two.log /srv/app/three.log
one.log
two.log
three.log
-s or --suffix also switches on multiple-name mode, which is convenient for applying one suffix to a list:
$ basename --suffix=.log /var/log/app.log /var/log/worker.log
app
worker
Tip
Do not confuse basename NAME SUFFIX with a two-item list. Without -a or -s, the second argument is the suffix for the single first name, which is a common trap when a command gets built up incrementally.
5. Preserve unusual names with NUL output
Newline-delimited output is readable, but a filename can legally contain a newline. When another program will consume the output, use -z or --zero so each result ends with a NUL byte:
$ basename -z -a /tmp/one.txt /tmp/two.txt | od -An -t x1
6f 6e 65 2e 74 78 74 00 74 77 6f 2e 74 78 74 00
The two 00 bytes are the record separators. Pair this mode with a NUL-aware consumer, such as GNU xargs -0:
basename -z -a /srv/app/one.log /srv/app/two.log |
xargs -0 -r printf 'name: <%s>\n'
Expected output is:
name: <one.log>
name: <two.log>
Warning
This protects the boundary between records, but it does not make an unsafe downstream command safe. Quote shell variables, avoid building a shell command from untrusted names, and stop to review the whole pipeline before anything that deletes or overwrites files. Nothing in these examples changes filesystem state.
6. Check the result in a script
Capture the output when you need to use it later, and keep the input and suffix separate:
path='/srv/app/releases/v4.2.tar.gz'
name=$(basename "$path" '.tar.gz')
if [ "$name" = 'v4.2' ]; then
printf 'release: %s\n' "$name"
else
printf 'unexpected name: %s\n' "$name" >&2
exit 1
fi
Expected output:
release: v4.2
Most failures here are shell errors rather than basename errors: an unquoted variable splitting into several arguments, an empty suffix that does not express the rule you intended, or a command substitution hiding a failing pipeline if you only look at its text. Keep transformations small and check the value at the point where it matters.
Done means
- You strip directories with one quoted name and know the path need not exist.
- You remove an exact trailing suffix without treating it as a wildcard pattern.
- You use
-aor-swhen processing several names. - You use
-zwith a NUL-aware consumer when newlines could be part of a name. - You checked the transformed value before passing it to any command that changes state.