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:
http://www.shelldorado.com/
View previous issues at the following location:
http://www.shelldorado.com/newsletter/
"Heiner's SHELLdorado" is a place for UNIX shell script
programmers providing
Many shell script examples,
shell scripting tips & tricks + more...
================================================================
Contents
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:
http://www.shelldorado.com/search/
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:
http://www.shelldorado.com/search/search-swish.cgi
o The "Shell Scripts" section now lists more than 200 scripts:
http://www.shelldorado.com/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:
http://www.shelldorado.com/links/
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
<heiner.steven@shelldorado.com>
-----------------------------------------------------------------
>> 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
then
rm requests.log
fi
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
condition:
mkdir /tmp/work || exit 2 # no use in continuing
This is a short and readable way to write
if mkdir /tmp/work
then
: # success
else
exit 2
fi
-----------------------------------------------------------------
>> 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
PATH=$1
else
if echo "$PATH" | grep -v "$1" >/dev/null 2>&1
then
PATH=$PATH:$1
fi
fi
fi
}
Done! Now lets add the PATH we need:
PATH=
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 <rf@ironix.org>]
-----------------------------------------------------------------
>> 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.
bart
lisa
bart
bart
homer
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
changed.
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
running.
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.
Example:
#! /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,
e.g.
function progressindicator.alarm { date; }
[ Thanks to Brian Hiles <bsh@rawbw.com> and Jon LaBadie
<jon@jgcomp.com> 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
mailto:heiner.steven@shelldorado.com
================================================================
To unsubscribe send a mail with the body "unsubscribe" to
newsletter@shelldorado.com
================================================================