Four fun Unix commands

RELATED TOPICS

Unix Insider –

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

<font face="Courier">basename</font>
command will strip off the directory portion of a filename path and return only the filename itself. In the following example,
<font face="Courier">basename</font>
is used to strip the directory path from the filename so it returns simply
<font face="Courier">file.txt</font>
.

<font face="Courier">$ basename /this/is/a/file.txt
file.txt
$
</font>

In the example above, the file and path don't necessarily even exist. The

<font face="Courier">basename</font>
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.

<font face="Courier">$ basename /this/is/a/file/
file
$
 </font>

The

<font face="Courier">basename</font>
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
<font face="Courier">for name</font>
loop, the code to do this would look as follows (the listing is numbered for explanatory purposes):

<font face="Courier"> 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
 </font>

The

<font face="Courier">basename</font>
command is used to extract just the filename portion into the variable
<font face="Courier">fname</font>
. From there, the
<font face="Courier">$fname</font>
variable is used to construct various tests and copies. The
<font face="Courier">for name</font>
loop at line 1 sets the variable
<font face="Courier">$name</font>
to each filename that matches /docs/new/* and then executes the logic between lines 2 and 13. At line 3
<font face="Courier">basename</font>
is used to extract just the file portion of the name into the variable
<font face="Courier">$fname</font>
. 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
<font face="Courier">for name</font>
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

<font face="Courier">basename</font>
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,
<font face="Courier">.txt</font>
is added after the filename. The return from
<font face="Courier">basename</font>
is the single word file:

<font face="Courier">$ basename /this/is/a/file.txt .txt
file
$
</font>

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:

<font face="Courier"> 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
</font>

At line 1, the name of every doc file in /docs/for_comment is extracted into the

<font face="Courier">$name</font>
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

<font face="Courier">dirname</font>
utility is the complement of
<font face="Courier">basename</font>
. It returns the "path" component of a file pathname string, as shown in the following listing. In the second
<font face="Courier">dirname</font>
example,
<font face="Courier">dirname</font>
also removes the trailing slash before attempting to understand the string. The result is that the
<font face="Courier">dirname</font>
portion of
<font face="Courier">/x/y/z/</font>
is
<font face="Courier">/x/y</font>
:

<font face="Courier">$ dirname /this/is/a/file.txt
/this/is/a
dirname /x/y/z/
/x/y
$
</font>

<font face="Courier">dirname</font>
has no options for the command and simply returns the directory portion of a file path. It is not used as often as
<font face="Courier">basename</font>
.

Looking at performance with time

The

<font face="Courier">time</font>
command is excellent for analyzing the performance of a shell script or command. Simply type
<font face="Courier">time</font>
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.

<font face="Courier">$ time bigjob.sh
real 10m 10.55s
user  4m 08.47s
sys   1m 12.14s
 </font>

Some older versions of

<font face="Courier">time</font>
report the results in seconds only, as in the following listing:

<font face="Courier">$ time bigjob.sh
real  610.55
user  248.47
sys    72.14
</font>

tee

The

<font face="Courier">tee</font>
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
<font face="Courier">cat</font>
to type out the dir.txt file shows that it contains the same information that was displayed on the screen.

<font face="Courier">$ 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
$
</font>

Normally

<font face="Courier">tee</font>
creates the named file newly, but the
<font face="Courier">-a</font>
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:

<font face="Courier">$ 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
$
</font>

The

<font face="Courier">tee</font>
utility is very useful for logging messages while still allowing them to display on the screen. Assuming that
<font face="Courier">job.sh</font>
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.

<font face="Courier">$ 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
$
</font>

The

<font face="Courier">tee</font>
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.

RELATED TOPICS
What’s wrong? The new clean desk test
View Comments
You Might Like
Join the discussion
Be the first to comment on this article. Our Commenting Policies