390 lines
21 KiB
HTML
390 lines
21 KiB
HTML
<!--startcut ==============================================-->
|
|
<!-- *** BEGIN HTML header *** -->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<HTML><HEAD>
|
|
<title>Introduction to Shell Scripting LG #58</title>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
|
ALINK="#FF0000">
|
|
<!-- *** END HTML header *** -->
|
|
|
|
<CENTER>
|
|
<A HREF="http://www.linuxgazette.com/">
|
|
<H1><IMG ALT="LINUX GAZETTE" SRC="../gx/lglogo.jpg"
|
|
WIDTH="600" HEIGHT="124" border="0"></H1></A>
|
|
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen2.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue58/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="okopnik2.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
<P>
|
|
</CENTER>
|
|
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4 ALIGN="center">
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
<!--===================================================================-->
|
|
|
|
<center>
|
|
<H1><font color="maroon">Introduction to Shell Scripting</font></H1>
|
|
<H4>By <a href="mailto:ben-fuzzybear@yahoo.com">Ben Okopnik</a></H4>
|
|
</center>
|
|
<P> <HR> <P>
|
|
|
|
<!-- END header -->
|
|
|
|
|
|
|
|
|
|
<BLOCKQUOTE>
|
|
<STRONG> "You wouldn't believe how many managers believe that you can
|
|
get a baby in one month by making nine women pregnant."</STRONG><BR>
|
|
<CITE> -- Marc Wilson</CITE>
|
|
</BLOCKQUOTE>
|
|
|
|
<H2>Random Wanderings</H2>
|
|
<p>Well, this should be the last article in the "Introduction to Shell
|
|
Scripting" series - I've had great feedback from a number of readers (and
|
|
thank you all for your kind comments!), but we've covered most of the <b>basics</b>
|
|
of shell scripting; that was the original purpose of the series. I may
|
|
yet pop up at some point in the future ("Oh, rats, I forgot to explain
|
|
XYZ!"), but those of you who've been following along should now consider
|
|
yourselves Big-Time Experts, qualified to carry a briefcase and sound important...
|
|
<grin> Well, at least you should have a pretty good idea
|
|
of how to write a script and make it work - and that's a handy skill.
|
|
|
|
<H2>A Valued Assistant</H2>
|
|
<p>Quite a while ago, I found myself in a quandary while writing a script
|
|
(NO-O-O! How unusual! <grins>); I had an array that contained a list
|
|
of command lines that I needed to execute based on certain conditions.
|
|
I could read the array easily enough, or print out any of the variables
|
|
- but what I needed was to execute them! What to do, what to do... as I
|
|
remember, I gave up for lack of that one capability, and rewrote the whole
|
|
(quite large) script (it was <u>not</u> a joyful experience). "eval" would
|
|
have been the solution.
|
|
<p>Here's how it works - create a variable called $cmd, like so:
|
|
<p><font face="Courier New,Courier">cmd='cat .bashrc|sort'</font>
|
|
<p>It's just an example - you could use any valid command(s). Now, you
|
|
can echo the thing -
|
|
<p><font face="Courier New,Courier">Odin:~$ echo $cmd</font>
|
|
<br><font face="Courier New,Courier">cat .bashrc|sort</font>
|
|
<br><font face="Courier New,Courier">Odin:~$</font>
|
|
<p>- but how do you execute it? Just running "cmd" produces an error:
|
|
<p><font face="Courier New,Courier">Odin:~$ $cmd</font>
|
|
<br><font face="Courier New,Courier">cat: .bashrc|sort: No such file or
|
|
directory</font>
|
|
<br><font face="Courier New,Courier">Odin:~$</font>
|
|
<p>This is where "eval" comes into its own: "<font face="Courier New,Courier">eval
|
|
$cmd"</font> would evaluate the content of the variable as if it had been
|
|
entered at the command line. This is not something that comes up too often...
|
|
but it is a capability of the shell that you need to be aware of.
|
|
|
|
<P> Note that "bash" has no problem executing a single command that is
|
|
stored as a variable, something like:
|
|
|
|
<PRE>
|
|
Odin:~$ N="cat .bashrc"
|
|
Odin:~$ $N
|
|
# ~/.bashrc: executed by bash(1) for non-login shells.
|
|
|
|
export PS1='\h:\w\$ '
|
|
umask 022
|
|
</PRE>
|
|
|
|
works fine. It's only when more complex commands, e.g., those that involve
|
|
+aliases or operators ("|", ">", ">>", etc.) are used that
|
|
you would encounter problems - and for those times, "eval" is the answer.
|
|
|
|
|
|
<H2>Trapped Like a Rat</H2>
|
|
<p>One of the standard techniques in scripting (and in programming in general)
|
|
is that of writing data to temporary files - there are many reasons to
|
|
do this. But, and this is a big one, what happens when your users interrupt
|
|
that script halfway through execution? (For those of you who have scripts
|
|
like that and haven't thought of the issue, sorry to give you material
|
|
for nightmares. At least I'll show you the solution as well.)
|
|
<p>You guessed it: a mess. Lots of files in "/tmp", perhaps important data
|
|
left hanging in the breeze (to be deleted at next reboot), files thought
|
|
to be updated that are not... Yuck. How about a way for us to exit gracefully,
|
|
despite a frantic keyboard-pounding user who just <u>has</u> to run "Quake"
|
|
RIGHT NOW?
|
|
<p>The "trap" command provides an answer of sorts (shooting said user is
|
|
far more effective and enjoyable, but may get you talked about).
|
|
<p>
|
|
<hr WIDTH="100%">
|
|
<br><font face="Courier New,Courier">#!/bin/bash</font>
|
|
<p><font face="Courier New,Courier">function cleanup ()</font>
|
|
<br><font face="Courier New,Courier">{</font>
|
|
<br><font face="Courier New,Courier"> stty intr "" #
|
|
Ignore 'Ctrl-C'; let him pound away...</font>
|
|
<br><font face="Courier New,Courier"> echo "Wake up,
|
|
Neo."</font>
|
|
<br><font face="Courier New,Courier"> sleep 2; clear</font>
|
|
<br><font face="Courier New,Courier"> echo "The Matrix
|
|
has you."</font>
|
|
<p><font face="Courier New,Courier"> echo "He's at it
|
|
again."|mail admin -s "Update stopped by $USER"</font>
|
|
<p><font face="Courier New,Courier"> # Restore the original
|
|
data</font>
|
|
<br><font face="Courier New,Courier"> tar xvzf /mnt/backup/accts_recvbl
|
|
-C /usr/local/acct</font>
|
|
<br><font face="Courier New,Courier"> # Delete 'tmp'
|
|
stuff</font>
|
|
<br><font face="Courier New,Courier"> rm -rf /tmp/in_process/</font>
|
|
<p><font face="Courier New,Courier"> # OK, we've taken
|
|
care of the cleanup. Now, it's REVENGE time!!!</font>
|
|
<br><font face="Courier New,Courier"> rm /usr/games/[xs]quake</font>
|
|
<p><font face="Courier New,Courier"> # Give him a nice
|
|
new easy-to-remember password...</font>
|
|
<br><font face="Courier New,Courier"> chpasswd $USER:~X%y!Z@zF%HG72F8b@Idiot&(~64sfgrnntQwvff########^</font>
|
|
<p><font face="Courier New,Courier"> # We'll back up
|
|
all his stuff... Oh, what's "--remove-files" do?</font>
|
|
<br><font face="Courier New,Courier"> tar cvz --remove-files
|
|
-f /mnt/timbuktu/bye-bye.tgz /home/$USER</font>
|
|
<p><font face="Courier New,Courier"> # Heh-heh-heh...</font>
|
|
<br><font face="Courier New,Courier"> umount /mnt/timbuktu</font>
|
|
<p><font face="Courier New,Courier"> stty intr ^C # Back
|
|
to normal</font>
|
|
<br><font face="Courier New,Courier"> exit
|
|
# Yep, I meant to do that... Kill/hang the shell.</font>
|
|
<br><font face="Courier New,Courier">}</font>
|
|
<p><font face="Courier New,Courier">trap 'cleanup' 2</font>
|
|
<br>
|
|
<br><font face="Courier New,Courier">...</font>
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
<p>There's a little of the BOfH inside every admin. <grin> (For those
|
|
of you not familiar with the "BOfH Saga", this is a <b>must</b> read for
|
|
every Unix admin; appalling and hideously funny. Search the Web.)
|
|
<p>DON'T run this script... yes, I know it's tempting. The point of "trap"
|
|
is, we can define a behavior whenever the user hits `Ctrl-Break' (or for
|
|
that matter, any time the script exits or is killed) that is much more
|
|
useful to us than just crashing out of the program; it gives us a chance
|
|
to clean up, generate warnings, etc.
|
|
<p>"trap" can also catch other signals; the fact is that even "kill", despite
|
|
its name, does not of itself `kill' a process - it sends it a signal. The
|
|
process then decides what to do with that signal (a crude description,
|
|
but generally correct). If you wish to see the entire list of signals,
|
|
just type "<font face="Courier New,Courier">trap -l</font>" or "<font face="Courier New,Courier">kill
|
|
-l</font>" or even "<font face="Courier New,Courier">killall -l</font>"
|
|
(which does not list the signal numbers, just names). The ones most commonly
|
|
used are 1)SIGHUP, 2)SIGINT, 3)SIGQUIT, 9)SIGKILL, and 15)SIGTERM.
|
|
|
|
<BLOCKQUOTE><EM>
|
|
[But SIGKILL is untrappable. -Ed.]
|
|
</EM></BLOCKQUOTE>
|
|
|
|
|
|
<p>There are also the `special' signals. They are: 0)EXIT, which traps
|
|
on any exit from the shell, and DEBUG (no number assigned), which can -
|
|
here's a nifty thing! - be used to troubleshoot shell scripts (it traps
|
|
every time a simple command is executed). DEBUG is actually more of an
|
|
"info only" item: you can have this exact action without writing any "trap"s,
|
|
simply by adding "-x" to your "hash-bang" (see "IN CASE OF TROUBLE..."
|
|
below).
|
|
<p>"trap" is a powerful little tool. In LG#37, Jim Dennis had a short script
|
|
fragment that created a
|
|
<A HREF="../issue37/lg_answer37.html#tag_greeting">secure directory under "/tmp"</A> for just this sort
|
|
of thing - temp files that you don't want exposed to the world. Pretty
|
|
cool gadget; I've used it a number of times already.
|
|
|
|
<H2>In Case of Trouble, Break Glass</H2>
|
|
<p>Speaking of troubleshooting, "bash" provides several very useful tools
|
|
that can help you find the errors in your script. These are switches -
|
|
part of the "set" command syntax - that are used in the "hash-bang" line
|
|
of the script itself. These switches are:
|
|
<p><font face="Courier New,Courier">-n Read the
|
|
shell script lines, but do not execute</font>
|
|
<br><font face="Courier New,Courier">-v Print the
|
|
lines as they're read</font>
|
|
<br><font face="Courier New,Courier">-x Prints
|
|
$PS4 (the "level of indirection" prompt) and the command just executed.</font>
|
|
<p>I've found that "-nv" and "-x" are the most useful invocations: one
|
|
gives you the exact location of a "bad" line (you can see where the script
|
|
would crash); the other, `noisy' though it is, is handy for seeing where
|
|
things aren't happening quite the right way (when, even though the syntax
|
|
is right, the action is not what you want). Good troubleshooting tools
|
|
both. As time passes and you get used to the quirks of error reporting,
|
|
you'll probably use them less and less, but they're invaluable to a new
|
|
shell script writer.
|
|
<br>To use them, simply modify the initial "hash-bang":
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
<br><font face="Courier New,Courier">#!/bin/bash -nv</font>
|
|
<br><font face="Courier New,Courier">...</font>
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
|
|
<H2>Use the Source, Luke</H2>
|
|
<p>Here's a line familiar to every "C" programmer:
|
|
<p><font face="Courier New,Courier">#include <"stdio.h"></font>
|
|
<p>- a very useful concept, that of <u>sourcing external files</u>. What
|
|
that means is that a "C" programmer can write routines (functions) that
|
|
he'll use over and over again, store them in a `library' (an external file),
|
|
and bring them in as he needs them. Well - have I not said that shell scripting
|
|
is a mature, capable programming language? - we can do the same thing!
|
|
The file doesn't even have to be executable; the syntax that we use in
|
|
bringing it in takes care of that. The example below is a snippet of the
|
|
top of my function library, "Funky". Currently, it is a single file, a
|
|
couple of kB long, and growing apace. I try to keep it down to the most
|
|
useful functions, as I don't want to garbage up the environment space (is
|
|
the concept even applicable in Linux? Must find out...)
|
|
<p>There's a tricky little bit of "bash" maneuvering that's worth knowing:
|
|
if you create a variable called <font face="Courier New,Courier">BASH_ENV</font>
|
|
in your .bash_profile, like so:
|
|
<p><font face="Courier New,Courier">export BASH_ENV="~/.bash_env"</font>
|
|
<p>then create a file called ".bash_env" in your home directory, that file
|
|
will be re-read every time you start a `non-login non-interactive shell',
|
|
i.e., a shell script. A good place to put initialization stuff that is
|
|
shell-script specific; that's where I source "Funky" from - that way, any
|
|
changes in it are immediately available to any shell script.
|
|
<p>
|
|
<hr WIDTH="100%"><font face="Courier New,Courier">func_list ()
|
|
# lists all the functions in Funky</font>
|
|
<br><font face="Courier New,Courier">{</font>
|
|
<br><font face="Courier New,Courier"> # Any time I need
|
|
a reminder of what functions I have, what</font>
|
|
<br><font face="Courier New,Courier"> # they're called,
|
|
and what they do, I just type "func_list".</font>
|
|
<br><font face="Courier New,Courier"> # A cute example
|
|
of recursion - a func that lists all funcs,</font>
|
|
<br><font face="Courier New,Courier"> # including itself.</font>
|
|
<p><font face="Courier New,Courier"> cat /usr/local/bin/Funky|grep
|
|
\(\)</font>
|
|
<br><font face="Courier New,Courier">}</font>
|
|
<p><font face="Courier New,Courier">getch () #
|
|
gets one char from kbd, no "Enter" necessary</font>
|
|
<br><font face="Courier New,Courier">{</font>
|
|
<br><font face="Courier New,Courier"> OLD_STTY=`stty
|
|
-g`</font>
|
|
<br><font face="Courier New,Courier"> stty cbreak -echo</font>
|
|
<br><font face="Courier New,Courier"> GETCH=`dd if=/dev/tty
|
|
bs=1 count=1 2>/dev/null`</font>
|
|
<br><font face="Courier New,Courier"> stty $OLD_STTY</font>
|
|
<br><font face="Courier New,Courier">}</font>
|
|
<p><font face="Courier New,Courier">...</font>
|
|
<p>
|
|
<hr WIDTH="100%">
|
|
<p>Not too different from a script, is it? No "hash-bang" is necessary,
|
|
since this file does not get executed by itself. So, how do we use
|
|
it in a script? Here it is (we'll pretend that I don't source "Funky" in
|
|
".bash_env"):
|
|
<p>
|
|
<hr WIDTH="100%"><font face="Courier New,Courier">#!/bin/bash</font>
|
|
<p><font face="Courier New,Courier">. Funky</font>
|
|
<p><font face="Courier New,Courier">declare -i Total=0</font>
|
|
<p><font face="Courier New,Courier">leave ()</font>
|
|
<br><font face="Courier New,Courier">{</font>
|
|
<br><font face="Courier New,Courier"> echo "So youse
|
|
are done shoppin'?"</font>
|
|
<br><font face="Courier New,Courier"> [ $Total -ne 0
|
|
] && echo "Dat'll be $Total bucks, pal."</font>
|
|
<br><font face="Courier New,Courier"> echo "Have a nice
|
|
day."</font>
|
|
<br><font face="Courier New,Courier"> exit</font>
|
|
<br><font face="Courier New,Courier">}</font>
|
|
<p><font face="Courier New,Courier">trap 'leave' 0</font>
|
|
<br><font face="Courier New,Courier">clear</font>
|
|
<p><font face="Courier New,Courier">while [ 1 ]</font>
|
|
<br><font face="Courier New,Courier">do</font>
|
|
<br><font face="Courier New,Courier"> echo</font>
|
|
<br><font face="Courier New,Courier"> echo "Whaddaya
|
|
want? I got Cucumbers, Tomatoes, Lettuce, Onions,"</font>
|
|
<br><font face="Courier New,Courier"> echo "and Radishes
|
|
today."</font>
|
|
<br><font face="Courier New,Courier"> echo</font>
|
|
<p><font face="Courier New,Courier"> # Here's where we
|
|
call a sourced function...</font>
|
|
<br><font face="Courier New,Courier"> getch</font>
|
|
<p><font face="Courier New,Courier"> # ...and reference
|
|
a variable created by that function.</font>
|
|
<br><font face="Courier New,Courier"> case $GETCH</font>
|
|
<br><font face="Courier New,Courier"> in</font>
|
|
<br><font face="Courier New,Courier">
|
|
C|c) Total=$Total+1; echo "Them are good cukes." ;;</font>
|
|
<br><font face="Courier New,Courier">
|
|
T|t) Total=$Total+2; echo "Ripe tomatoes, huh?" ;;</font>
|
|
<br><font face="Courier New,Courier">
|
|
L|l) Total=$Total+2; echo "I picked da lettuce myself." ;;</font>
|
|
<br><font face="Courier New,Courier">
|
|
O|o) Total=$Total+1; echo "Fresh enough to make youse cry!" ;;</font>
|
|
<br><font face="Courier New,Courier">
|
|
R|r) Total=$Total+2; echo "Real crispy radishes." ;;</font>
|
|
<br><font face="Courier New,Courier">
|
|
*) echo "Ain't got nuttin' like that today, mebbe tomorra." ;;</font>
|
|
<br><font face="Courier New,Courier"> esac</font>
|
|
<p><font face="Courier New,Courier"> sleep 2</font>
|
|
<br><font face="Courier New,Courier"> clear</font>
|
|
<p><font face="Courier New,Courier">done</font>
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
<p>Note the period before "Funky": that's an alias for the "source" command.
|
|
When sourced, "Funky" acquires an interesting property: just as if we had
|
|
asked "bash" to execute a file, it goes out and searches the path listed
|
|
in $PATH. Since I keep "Funky" in "/usr/local/bin" (part of my $PATH),
|
|
I don't need to give an explicit path to it.
|
|
<p>If you're going to be writing shell scripts, I strongly suggest that
|
|
you start your own `library' of functions. (HINT: Steal the functions from
|
|
the above example!) Rather than typing them over and over again, a single
|
|
"source" argument will get you lots and lots of `canned' goodies.
|
|
|
|
<H2>Wrapping Up the Series</H2>
|
|
<p>Well - overall, lots of topics covered, some "quirks" explained; all
|
|
good stuff, useful shell scripting info. There's a lot more to it - remember,
|
|
this series was only an <u>introduction</u> to shell scripting - but anyone
|
|
who's stuck with me from the beginning and persevered in following my brand
|
|
of pretzel-bending logic (poor fellows! irretrievably damaged, not even
|
|
the best psychologist in the world can help you now... :) should now be
|
|
able to design, write, and troubleshoot a fairly decent shell script. The
|
|
rest of it - understanding and writing the more complex, more involved
|
|
scripts - can only come with practice, otherwise known as "making lots
|
|
of mistakes". In that spirit, I wish you all lots of "mistakes"!
|
|
<br>
|
|
<p>Happy Linuxing!
|
|
<p>
|
|
<hr WIDTH="100%">Linux Quote of the Month:
|
|
<br><font face="Courier New,Courier">``The words "community" and "communication"
|
|
have the same root.</font>
|
|
<br><font face="Courier New,Courier">Wherever you put a communications
|
|
network, you put a community as</font>
|
|
<br><font face="Courier New,Courier">well. And whenever you <b>take away</b>
|
|
that network -- confiscate it, outlaw it, crash it, raise its price beyond
|
|
affordability -- then you hurt that community.</font><font face="Courier New,Courier"></font>
|
|
<p><font face="Courier New,Courier">Communities will fight to defend
|
|
themselves. People will fight harder and more bitterly to defend
|
|
their communities, than they will fight to defend their own individual
|
|
selves.''</font>
|
|
<br><font face="Courier New,Courier"> -- Bruce Sterling, "Hacker Crackdown"</font>
|
|
<br>
|
|
<hr WIDTH="100%">
|
|
<br>REFERENCES
|
|
<p>The "man" pages for 'bash', 'builtins', 'stty'
|
|
<A HREF="../issue53/okopnik.html">"Introduction to Shell Scripting - The Basics", LG #53</A><BR>
|
|
<A HREF="../issue54/okopnik.html">"Introduction to Shell Scripting", LG #54</A><BR>
|
|
<A HREF="../issue55/okopnik.html">"Introduction to Shell Scripting", LG #55</A><BR>
|
|
<A HREF="../issue57/okopnik.html">"Introduction to Shell Scripting", LG #57</A><BR>
|
|
|
|
|
|
|
|
|
|
<!-- *** BEGIN copyright *** -->
|
|
<P> <hr> <!-- P -->
|
|
<H5 ALIGN=center>
|
|
|
|
Copyright © 2000, Ben Okopnik<BR>
|
|
Published in Issue 58 of <i>Linux Gazette</i>, October 2000</H5>
|
|
<!-- *** END copyright *** -->
|
|
|
|
<!--startcut ==========================================================-->
|
|
<HR><P>
|
|
<CENTER>
|
|
<!-- *** BEGIN navbar *** -->
|
|
<IMG ALT="" SRC="../gx/navbar/left.jpg" WIDTH="14" HEIGHT="45" BORDER="0" ALIGN="bottom"><A HREF="nielsen2.html"><IMG ALT="[ Prev ]" SRC="../gx/navbar/prev.jpg" WIDTH="16" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="index.html"><IMG ALT="[ Table of Contents ]" SRC="../gx/navbar/toc.jpg" WIDTH="220" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../index.html"><IMG ALT="[ Front Page ]" SRC="../gx/navbar/frontpage.jpg" WIDTH="137" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="http://www.linuxgazette.com/cgi-bin/talkback/all.py?site=LG&article=http://www.linuxgazette.com/issue58/okopnik.html"><IMG ALT="[ Talkback ]" SRC="../gx/navbar/talkback.jpg" WIDTH="121" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><A HREF="../faq/index.html"><IMG ALT="[ FAQ ]" SRC="./../gx/navbar/faq.jpg"WIDTH="62" HEIGHT="45" BORDER="0" ALIGN="bottom"></A><A HREF="okopnik2.html"><IMG ALT="[ Next ]" SRC="../gx/navbar/next.jpg" WIDTH="15" HEIGHT="45" BORDER="0" ALIGN="bottom" ></A><IMG ALT="" SRC="../gx/navbar/right.jpg" WIDTH="15" HEIGHT="45" ALIGN="bottom">
|
|
<!-- *** END navbar *** -->
|
|
</CENTER>
|
|
</BODY></HTML>
|
|
<!--endcut ============================================================-->
|