Unix How To: Using Basename Wisely

At its simplest, the basename command will strip the directory structure off the front of a file reference. /usr/bin/passwd becomes passwd. /usr/local/bin/myscript becomes myscript. Easy enough. You get an extra bonus when you follow the command with a file extension. The basename command will strip that off as well. For example, you can strip off the .conf extension from a file like this:

bozon$ basename /etc/nsswitch.conf .conf
nsswitch

This little trick can help if you want to make backup copies of some files and want to change the extension rather than tack on a second one. For example:

boson$ ls *pl
findAppl   lwday.pl   parse3.pl  parse4.pl
boson$ for file in `ls *.pl`
> do
>     fname=`basename $file .pl`
>     cp $file $fname.bak
> done
boson$ ls *bak
lwday.bak   parse3.bak  parse4.bak

It helps to understand that basename doesn't really know about your files. Ask basename to remove the path from /north/pole/SantaClaus and it will respond with "SantaClaus". It's not going to come back and tell you that SantaClaus doesn't exist, spit out "file not found" or chastise you with a usage statement just because you don't have any directories that even vaguely resemble /north/pole. It just works with patterns. The basename command's only job is to chop off everything in a string up to and including the last forward slash.

That is, unless your path includes spaces! Try basename on a path that includes spaces and it will ignore everything following the first space as it takes everything prior to the space as its argument and ignores the rest:

boson$ basename /export/home/Santa Claus/gift.list
Santa

Of course, most Unix dweebs don't create directories with spaces in their names, so this isn't a problem that many of us will run across. But put your filename in quotes (e.g., "/export/home/Santa Claus/gift.lst" or "$filename") and you won't have this problem.

The basename command is also quite robotic in its processing of file extensions. Give the command .log, .txt, .conf or any in the slew of normal file extensions we use routinely and it deftly lops off the file extension along with the pesky dot. Give it some extra text and it lops off that as well:

$ basename ./final.log
final.log
$ basename ./final.log .log
final
$ basename ./final.log l.log
fina

OK, maybe that will little trick come it handy when I want to change all filenames ending in 2009.

to 2010..
boson$ fname=`basename tasks2009.log 09.log`
boson$ touch ${fname}10.log
boson$ ls -l *2010*
-rw-r--r--   1 shs  staff          0 Dec 19 12:47 tasks2010.log
Ready for another year! As much as the basename command is good at its only focus in life, it does come with a bit of a price tag. That price tag is measured in performance. Use filename to lop the path off a file or two and you can go to sleep knowing Santa is not going to put anything nasty into your stocking. Use basename to lop the paths off a few thousand files and you might find that you are using up four or five times as much computer cycles than you would if you were doing the same thing with shell substitution. For example, like this:
#!/bin/bash

while read file
do
  echo "${file##*/}";
done < /tmp/files
The basic reason is, of course, that each call to basename initiates another process (it's a separate command, after all, not part of your shell environment). Santa Claus might not bring me that scroll saw I put on my Christmas list, but I am unlikely to get a stocking full of coal if I'm careful not to waste computer cycles! I hope you get everything you're hoping for this holiday season whether or not you still believe in the Jolly Old Elf!
What’s wrong? The new clean desk test
Join the discussion
Be the first to comment on this article. Our Commenting Policies