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.

 

MENU:

 
SOFTWARE DEVELOPMENT:
    • EXPERIENCE
PRODUCTS:
UNIX: 

   • UNIX TUTORIALS

LEGACY SYSTEMS:

    • LEARN COBOL
    • PRODUCTS
    • GEN-CODE
    • COMPILERS   

INTERNET:
    • CYBERSUITE   
WINDOWS:

    • PRODUCTS


Search Now:
 
In Association with Amazon.com

Copyright©2001 King Computer Services Inc. All rights reserved.