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.

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

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