1. Overview
In this tutorial, we’ll explore the basename command’s syntax, options, and real-world examples to master its usage.
2. Basic Usage
The basename command allows us to extract the filename component from a given path. Here’s its basic syntax:
basename [OPTIONS] NAME
An example can quickly show how we can use the command and what it does:
$ basename /some/path/theBaseName.txt
theBaseName.txt
By default, basename extracts the filename component from a provided path. While the syntax is straightforward, it’s essential to recognize that basename doesn’t validate whether the input represents a valid filesystem path.
For example, on our system, the directory /some/path doesn’t exist at all:
$ [[ -d "/some/path" ]] && echo "Exists." || echo "Directory Not Found."
Directory Not Found.
In the above command, we used && and || to output the message conditionally.
Moreover, if the input has a trailing slash, basename ignores the trailing slash:
$ basename /some/path/
path
However, there is a special case. If the input is the root directory “*/“, basename reports “/*“:
$ basename /
/
The basename command provides several useful options to fulfill different use cases. Next, let’s examine those options in more detail.
3. The -a Option
By default, the basename command only supports one single input, for example:
$ basename /some/path/file1.txt /some/path/file2.txt
file1.txt
The -a option tells basename to accept multiple filenames as arguments and process each input separately:
$ basename -a /some/path/file1.txt /some/path/file2.txt
file1.txt
file2.txt
4. The -s Option and Suffix Handling
The basename command also supports removing a suffix from the result. For a single input, we can execute the command using this syntax:
basename NAME SUFFIX
In practice, we often use this feature to remove file extensions:
$ basename /some/path/file1.txt .txt
file1
However, when we pass multiple path inputs to basename, this approach doesn’t work anymore:
$ basename -a /some/path/file1.txt /some/path/file2.txt .txt
file1.txt
file2.txt
.txt
This occurs because, with the -a option, basename cannot differentiate whether “*.txt*” is a suffix or another regular path input. To resolve this issue, we can pass the -s option to inform the basename command about the suffix information:
$ basename -s '.txt' /some/path/file1.txt /some/path/file2.txt
file1
file2
Sharp eyes may have noticed that we didn’t include the -a option with basename in the command above, even though we provided two path inputs. This is due to the -s option, which resolves the issue of determining suffixes in cases with multiple inputs. When using the -s option, basename inherently supports multiple inputs, rendering the -a option unnecessary.
5. The -z Option
Up to this point, we’ve explored the primary usages of basename. From the examples provided, we can see that basename employs newline characters as the default delimiters in its output. This default behavior is suitable for most scenarios. However, it can become confusing when our input contains special characters:
$ basename -a /some/path/$'file\n1'.txt /some/path/$'file\n2'.txt
file
1.txt
file
2.txt
In the example above, our two inputs contain newline characters (\n). We used the $’…’ approach to quote their names. As a result, basename‘s output displays four lines. This makes it challenging to distinguish which value corresponds to each input.
To solve this problem, we can use the -z option to ask basename to delimit output with NULL characters rather than newlines:
$ basename -az /some/path/$'file\n1'.txt /some/path/$'file\n2'.txt | cat -v
file
1.txt^@file
2.txt^@
To display the NULL characters, we piped basename‘s result to the cat command with the -v option, which prints NULL as ^@.
Now, if we need to process basename‘s result by further tools, we can define NULL as the delimiter in other commands to get the two filenames. For example, let’s pass the basename output to AWK to replace the newline character in each filename with a marker:
$ basename -az /some/path/$'file\n1'.txt /some/path/$'file\n2'.txt | awk -v RS="\0" '{sub(/\n/,"[newline]"); print "File: "$0}'
File: file[newline]1.txt
File: file[newline]2.txt
We designated the NULL character (\0) as the record separator (RS) in the awk one-liner. Consequently, we effectively differentiated between the two files in basename‘s output.
6. Conclusion
basename holds a significant place for its simplicity and versatility in handling file paths. In this article, we explored using it to extract the last component from path inputs through examples.