Four fun
Unix commands
A look at basename,
dirname, time, and tee
Summary
In this last of four columns on "small fry" Unix
commands, Mo shows you how to snip directory paths and
filename extensions with the basename and dirname
commands. He also explains how to use tee to
write the output from a piped command into a file. Finally, Mo
covers the time command, which can be used as a
simple performance measuring tool. (2,000 words)
This month marks the end of my series on
small but useful Unix commands. I don't mean to suggest that
Unix has no other useful commands, it's just that I've run out
of the small ones that I think are fun.
basename
The basename command will strip off the directory
portion of a filename path and return only the filename itself.
In the following example, basename is used to strip
the directory path from the filename so it returns simply file.txt .
$ basename /this/is/a/file.txt
file.txt
$
In the example above, the file and path don't necessarily
even exist. The basename command analyzes the
string and takes out the pieces it estimates to be the directory
path. Basically, it leaves the last piece of the string that
doesn't contain a slash (/). It also removes any trailing slash
in order to identify the base portion of the name. In the
following listing, the final slash is removed before evaluating
the string and returning the word file as the
"base" for the string.
$ basename /this/is/a/file/
file
$
The basename utility is very useful for situations
in which files are replicated across multiple directories. In a
hypothetical system, a series of directories contain the current
versions of documents, the newest versions of documents to
replace the current versions, and two earlier versions of the
same documents where they've been replaced. Assuming that the
directories are named new, current, old and oldest,
a process is needed to check the names of all the new documents
in the new directory. Any documents with the same name in the
old directory are moved to the oldest directory, any documents
with the same name in the current directory are moved to the old
directory, and finally the new documents are moved to the
current directory. Using a for name loop, the code
to do this would look as follows (the listing is numbered for
explanatory purposes):
1 for name in /docs/new/*
2 do
3 fname=`basename $name`
4 if [ -f /docs/old/$fname ]
5 then
6 mv /docs/old/$fname /docs/oldest/$fname
7 fi
8 if [ -f /docs/current $fname ]
9 then
10 mv /docs/current/$fname /docs/old/$fname
11 fi
12 mv /docs/new/$fname /docs/current/$fname
13 done
The basename command is used to extract just the
filename portion into the variable fname . From
there, the $fname variable is used to construct
various tests and copies. The for name loop at line
1 sets the variable $name to each filename that
matches /docs/new/* and then executes the logic between
lines 2 and 13. At line 3 basename is used to
extract just the file portion of the name into the variable $fname .
Once the base portion of the filename is identified, a series of
tests and moves can be executed. At line 4 the logic tests to
see if a file of the same name exists in the /docs/old
directory. If it does, it's moved to /docs/oldest at
line 6. This is repeated for the /docs/current and docs/old
directories at lines 8 through 11. Finally, at line 12 the file
in /docs/new is moved into /docs/current. The
logic at line 12 doesn't bother testing for the existence of the
file because the for name logic started at line 1
has established that something does exists in /docs/new.
This logic assumes that the /docs/new directory will
only contain files that are to be installed in /docs/current
and will not contain subdirectories.
Removing extensions
The basename utility also allows the extension or
suffix to be stripped from a file basename. The extension to be
stripped is added after the file path. In the following example,
.txt is added after the filename. The return from basename
is the single word file:
$ basename /this/is/a/file.txt .txt
file
$
To illustrate this hypothetically, assume that documents in
the master directory are copied to a holding directory for
comments. Any comments are written to a file with the extension .comment.
For example, a document named proposal.doc might have a
corresponding file named proposal.comment containing
any comments on the document.
A process to gather up the comments has two jobs to do. First
it must check that comments have been entered for a document and
remove the copy of the document and the comments. Its second
task is to raise an alert about any documents that have not been
commented on. A process to do this is illustrated in the
following numbered listing:
1 for name in /docs/for_comment/*.doc
2 do
3 bname=`basename $name .doc`
4 cname=${bname}.comment
5 dname=${bname}.doc
6 if [ -f /docs/for_comment/$cname ]
7 then
8 mv /docs/for_comment/$cname /docs/master/$cname
9 rm -f /docs/for_comment/$dname
9 else
10 echo "No comments received for $dname"
11 fi
12 done
At line 1, the name of every doc file in /docs/for_comment
is extracted into the $name variable. At line 2,
the filename is stripped of both the directory information, and
the file extension. At lines 4 and 5, this root is built up
again into two variables containing filename.doc and filename.comment.
At line 6, a test is made for a file with a .comment
extension. If one is found, the comment file is moved back into
the master directory, and the temporary version of the document
that was placed in /docs/for_comment is removed. If no
comment file is found, a message is displayed on the console
that comments are missing for the document. This is repeated for
each doc file in /docs/for_comment.
dirname
The dirname utility is the complement of basename .
It returns the "path" component of a file pathname
string, as shown in the following listing. In the second dirname
example, dirname also removes the trailing slash
before attempting to understand the string. The result is that
the dirname portion of /x/y/z/ is /x/y :
$ dirname /this/is/a/file.txt
/this/is/a
dirname /x/y/z/
/x/y
$
dirname has no options for the command and
simply returns the directory portion of a file path. It is not
used as often as basename .
Looking at performance with time
The time command is excellent for analyzing the
performance of a shell script or command. Simply type time
followed by the command that you wish to time. Three results are
printed when the program or script finishes executing: the
actual length of time (real-world time spent on the program),
the total time spent in the program, and the total time spent on
CPU overhead. The first figure is perhaps the most useful, but
the third figure will tell you how busy your CPU is.
$ time bigjob.sh
real 10m 10.55s
user 4m 08.47s
sys 1m 12.14s
Some older versions of time report the results
in seconds only, as in the following listing:
$ time bigjob.sh
real 610.55
user 248.47
sys 72.14
tee
The tee utility is one of my personal favorites and
is very simple. The command is intended to be used in a pipe to
capture the standard output of another command and display it on
the screen, as well as copy it to a file. In the following
example, the directory listing is displayed on the screen, and
also copied to the file dir.txt. Using cat
to type out the dir.txt file shows that it contains the
same information that was displayed on the screen.
$ ls -l|tee dir.txt
total 141
-rwxrwxrwx 1 mjb group 16850 Apr 12 16:13 SMALL01.DOC
-rwxrwxrwx 1 mjb group 14881 Apr 12 20:51 SMALL02.DOC
-rwxrwxrwx 1 mjb group 17758 Jun 13 01:29 Small03.doc
-rwxrwxrwa 1 mjb group 12791 Jul 12 22:44 Small04.doc
-rwxrwxrwx 1 mjb group 4232 Jun 12 00:03 Smallxx.doc
drwxrwxrwx 1 mjb group 0 Jul 12 21:25 docs
-rwxrwxrwx 1 mjb group 261 Jun 13 01:08 hello.cbl
-rwxrwxrwx 1 mjb group 184 Jun 13 00:59 hello.txt
-rwxrwxrwa 1 mjb group 343 Jul 12 21:32 mver
-rwxrwxrwa 1 mjb group 83 Jul 12 19:52 sh_histo
-rwxrwxrwx 1 mjb group 455 Apr 12 18:10 simpmenu
-rwxrwxrwx 1 mjb group 600 Apr 12 18:39 simpmenu.txt
-rwxrwxrwa 1 mjb group 17 Jul 12 22:24 sleepy
-rwxrwxrwx 1 mjb group 189 Jun 13 01:13 smallfry.txt
$ cat dir.txt
total 141
-rwxrwxrwx 1 mjb group 16850 Apr 12 16:13 SMALL01.DOC
-rwxrwxrwx 1 mjb group 14881 Apr 12 20:51 SMALL02.DOC
-rwxrwxrwx 1 mjb group 17758 Jun 13 01:29 Small03.doc
-rwxrwxrwa 1 mjb group 12791 Jul 12 22:44 Small04.doc
-rwxrwxrwx 1 mjb group 4232 Jun 12 00:03 Smallxx.doc
drwxrwxrwx 1 mjb group 0 Jul 12 21:25 docs
-rwxrwxrwx 1 mjb group 261 Jun 13 01:08 hello.cbl
-rwxrwxrwx 1 mjb group 184 Jun 13 00:59 hello.txt
-rwxrwxrwa 1 mjb group 343 Jul 12 21:32 mver
-rwxrwxrwa 1 mjb group 83 Jul 12 19:52 sh_histo
-rwxrwxrwx 1 mjb group 455 Apr 12 18:10 simpmenu
-rwxrwxrwx 1 mjb group 600 Apr 12 18:39 simpmenu.txt
-rwxrwxrwa 1 mjb group 17 Jul 12 22:24 sleepy
-rwxrwxrwx 1 mjb group 189 Jun 13 01:13 smallfry.txt
$
Normally tee creates the named file newly, but
the -a option causes the new information to be
appended to the file. For example the following command would
extend the dir.txt file that has just been created:
$ echo "Those are all the files"|tee -a dir.txt
Those are all the files
$ cat dir.txt
total 141
-rwxrwxrwx 1 mjb group 16850 Apr 12 16:13 SMALL01.DOC
-rwxrwxrwx 1 mjb group 14881 Apr 12 20:51 SMALL02.DOC
-rwxrwxrwx 1 mjb group 17758 Jun 13 01:29 Small03.doc
-rwxrwxrwa 1 mjb group 12791 Jul 12 22:44 Small04.doc
-rwxrwxrwx 1 mjb group 4232 Jun 12 00:03 Smallxx.doc
drwxrwxrwx 1 mjb group 0 Jul 12 21:25 docs
-rwxrwxrwx 1 mjb group 261 Jun 13 01:08 hello.cbl
-rwxrwxrwx 1 mjb group 184 Jun 13 00:59 hello.txt
-rwxrwxrwa 1 mjb group 343 Jul 12 21:32 mver
-rwxrwxrwa 1 mjb group 83 Jul 12 19:52 sh_histo
-rwxrwxrwx 1 mjb group 455 Apr 12 18:10 simpmenu
-rwxrwxrwx 1 mjb group 600 Apr 12 18:39 simpmenu.txt
-rwxrwxrwa 1 mjb group 17 Jul 12 22:24 sleepy
-rwxrwxrwx 1 mjb group 189 Jun 13 01:13 smallfry.txt
Those are all the files
$
The tee utility is very useful for logging
messages while still allowing them to display on the screen.
Assuming that job.sh displays information on the
screen while it's processing, the following listing will copy
everything that goes to the screen to the file job.log.
In this example the log file is set up with date and time
information and then the job is run appending the information to
the log file.
$ echo "Starting job.sh"|tee job.log
$ date|tee -a job.log
$ job.sh|tee -a job.log
Processing Check File
Check Entries Valid - No Errors
Printing Checks
End Of Job
$ cat job.log
Starting job.sh
Mon Jul 13 13:36:22 PDT 1998
Processing Check File
Check Entries Valid - No Errors
Printing Checks
End Of Job
$
The tee utility can cut down on a lot of double
logging, where one message is sent to the screen and another or
the same message is also captured in a log file.
Contact
us for a free consultation. |