SHELLdorado Newsletter 2/2002 - June 4th, 2002

The "SHELLdorado Newsletter" covers UNIX shell script related
topics. To subscribe to this newsletter, leave your e-mail
address at the SHELLdorado home page:

View previous issues at the following location:

"Heiner's SHELLdorado" is a place for UNIX shell script
programmers providing

     Many shell script examples,
     shell scripting tips & tricks + more...


 o  What's new at the SHELLdorado?
 o  Shell Tip: Shorter if..then..else
 o  Shell Tip: Setting PATH and MANPATH variable
 o  Shell Tip: Print each line exactly once
 o  How to use the undocumented "alarm" function (ksh93)

>> What's new at the SHELLdorado?

 o  You can now search the SHELLdorado for keywords using the
    new search facility:

    The starting page gives some tips on searching, but the
    usage is fairly intuitive. The CGI script used to process
    the form data and process the search results (of course a
    shell script) is available as an example for your own
    ventures into CGI scripting:

 o  The "Shell Scripts" section now lists more than 200 scripts:

 o  The "Links" page has more than 180 pointers to shell script
    examples, shell scripting tutorials and references and many
    more shell script related resources:

    If you have comments or suggestions for this newsletter, or
    even want to write an article by your own, please write me
    an e-mail.
    Heiner Steven, Editor

>> Shell Tip: Shorter if..then..else

    In shell scripts many commands only should be executed if
    a previous command completed successfully. If we e.g. create
    a backup copy, we only want to remove the original file if
    the backup was created:

    	if cp requests.log requests.bak
	    rm requests.log

    If the "then" part only consists of one statement, this can
    easily be shortened to

    	cp requests.log requests.bak &&
	    rm requests.log

    The command following "&&" will only be executed if the
    previous command completed successfully, i.e. returned with
    a zero exit code.

    The "||" command can be used similar to check for an error

    	mkdir /tmp/work || exit 2	# no use in continuing

    This is a short and readable way to write

    	if mkdir /tmp/work
	    :	# success
	    exit 2

>> Shell Tip: Setting PATH and MANPATH variable

    Assignments like PATH=/usr/bin:/usr/local/bin:/usr/ucb/bin
    are not very easy to maintain.  Using a function at this
    point will make life much easier.

    First we need a function for addpath:

	# construct PATH and MANPATH

	unset PATH # if PATH is set, unset it

	addpath() {
	    if [ -d "$1" ]; then
		if [ X"$PATH" = "X" ]; then
		    if echo "$PATH" | grep -v "$1" >/dev/null 2>&1

    Done! Now lets add the PATH we need:

	addpath /bin
	addpath /sbin
	addpath /usr/bin
	addpath /usr/sbin
	addpath $HOME/bin    # bin in HOMEDIR

    At last we have to export the variable:

	export PATH

    Thats it! Now we have a nice looking PATH.  Put this in your
    .profile or .bashrc and you are ready to go.

    [This tip was contributed by Reinhold Farsch <>]

>> Shell Tip: Print each line exactly once

    Sometimes we have a file with duplicate lines, and we want
    to print each line only once, e.g.


    Well, "uniq" is made for this purpose, but it only detects
    consecutive duplicate lines. We could use "sort -u", but
    sometimes we don't want the sequence of the input lines

    The following "awk" script prints each line once (the output
    being "bart lisa homer", each word on its own line) and does
    not change the sequence of the input lines:

        # printonce - print each line exactly once
    	awk '!L[$0]++' "$@"

    And now to something completely different -- well, maybe the
    cryptic line of seemingly random characters deserves some
    explanation ;-)

    The idea of the AWK script is to store each input line, and
    print it only the first time it is seen:

    	awk '
		# Store each line into line buffer L[], and
		# count how often it appeared as input:

	    	L[$0] = L[$0] + 1
		if ( L[$0] == 1 )
		    print $0

    This can be shortened to

    	awk 'L[$0]++ == 0 { print $0 }'

    The line counter is checked for the value 0 (the line has
    not been seen yet), and increased by one afterwards. The
    very first time, the input line is printed.

    But the action "print $0" is the default action for AWK,
    hence we can omit it:

    	awk 'L[$0]++ == 0'

    And if we have an boolean expression "e", e == 0 is the same
    as "!e", so we finally get our initial AWK one-liner:

    	awk '!L[$0]++'

>> How to use the undocumented "alarm" function (ksh93)

    If a script runs for a very long time (e.g. a backup or
    installation program), it's a good idea to print a progress
    indicator to let the impatient user know that it is still

    More generally speaking, we sometimes would like to have
    parts of our scripts run periodically (e.g. once a second or
    every minute). Although this is possible by starting a
    process in the background ("while sleep 1; do ...; done &"),
    the new KornShell (ksh93) has an easier (but undocumented)
    way of setting up interval timers:

    	alarm [-r] varname interval
	    -r: repeat alarm every "interval" seconds

    This build-in command sets up an interval timer that will
    call the "alarm" discipline function (see below) every
    "interval" seconds. Without the "-r" option, the function is
    called only once.


	#! /usr/local/bin/ksh93
    	alarm -r a +1
	function a.alarm { print -n "$(date +%H:%S)\r"; }
	read dummy	# wait

    This example prints the current time, updated every second.
    The "alarm" command has to be used in the following way (the
    order of the commands is significant):

     1.	Set up the alarm timer using the "alarm" command, e.g.

	    alarm -r progressindicator +10

     2.	Define an "alarm" discipline function for the variable,

	    function progressindicator.alarm { date; }

    [ Thanks to Brian Hiles <> and Jon LaBadie
    <> for their help in solving the riddle of the
    mysterious "alarm" built-in command ]

If you want to comment on the newsletter, have suggestions for
new topics to be covered in one of the next issues, or even want
to submit an article of your own, send an e-mail to

To unsubscribe send a mail with the body "unsubscribe" to