Linux Supporters Group
Resources that help you to master GNU/Linux -- all the way from Australia !

Linux Supporters Group

Home | Blog | Interest | Tutorials

GNU/Linux Tutorial: "Develop a Log Script"


What is a Script?

A script is a text file containing a sequence of shell commands that you would otherwise type at the terminal. The script can be executed at any time or amended for future use.

Scripting in GNU/Linux is often done using the bash syntax.


What is Bash?

Bash is the Bourne-Again Shell. Popular on GNU/Linux systems as the main command interpreter, it is a computer program of more than 50,000 lines, written in the C language and currently maintained by Chet Ramey.

Here is a good overview of bash with links to other tutorials. There is also a long and complex bash manual for reference by non-beginners, and in which information can be found .... (If you dare, have a quick look at bash version 2.05a).

For these tutorials, you'll need to pick up the basic syntax of the Bourne-Again shell. I am not going to teach you any of that; we shall just be using it. You should read through any of the following: the excellent bash tutorial by Mendel Cooper which explains all these bash details, the very thorough linux tutorial by Bob Rankin that covers any topic you want to choose about Linux, browse this quick linux tutorial by Jiri Vogel which covers a lot of stuff usefully and quickly, and also see the Hamish Whittle bash tutorial. You'll absorb quite a bit by just doing the exercises in those references.


Why Script?

Good reasons include: All in all, learning even simple command line scripting makes for much greater productivity.

How to Script?

Top Down Design

Top down design is systematic and fast.

  1. Define what you want to do at the current level.
  2. If you can implement it in bash, implement it and test it.
  3. Otherwise break it down into smaller tasks and loop to 1.

You then go through that process again: you define everything you can and if you can implement it do it; else break it down again into pieces etc until all done in bash or until prove it cannot be done. That's when you might have to turn to the C language.

Two things you need to know before you plunge in here:

So hold on tight...


Specify What You Want to Do

We shall introduce the command line by developing a simple logging facility that you can use forever after.

This might be it for our example script:

  1. log a comment with date it was logged
  2. view all comments
  3. edit all comments
  4. print the comments

That completes the specification of the script. We can now move on to the subsystem design.


Determine How You Can Do It

This level normally answers the question "How are we going to do it?"

There are four separate things here to do:

  1. log a comment with date it was logged
  2. view all comments
  3. edit all comments
  4. print all comments

and we would want to do this with one script. We could create a separate script for each action but that is unnecessary and it makes more things to remember.

So can we do this in bash?

Right now you need to know a little about shell positional parameters.

Well, yes, it looks as if we can; we can name a script log and use it to execute the four parts of the task above, and we can distinguish each of the four tasks using the 'case' statement.

If we want our script to look bash-like then whenever we want to log a normal comment we could type something like

log

Whenever we want to just view the comments we could type something like

log -v

Whenever we want to edit them all we could type something like

log -e

Whenever we want to print them all we could type something like

log -p

Cooper's tutorial shows us that this structure should work OK with the case statement but [see note].

To summarize our commands:

command you type action requested script executed positional parameter 1
log log a comment log (null)
log -v view all comments log -v
log -e edit all comments log -e
log -p print all comments log -p

We shall consider anything else following 'log' to be a mistake, treat it as a request for help, and just display a reminder of the correct usage.

Here is the case statement so far in all its glory:

case $1 in
"") [log comment] ;;
-v) [view comments] ;;
-e) [edit comments] ;;
-p) [print comments] ;;
*) [display usage] ;;
esac

Notice in this:

We still have to fill in the details that are between [] in the script above.

Now, here is the great secret to top-down design: get this working at this level before continuing, and then you always know that the last change you made is the cause of any problems, and you never have to return to debug this level again. In other words -- this should work already as far as it can. So lets get this working.

If you want to try out this script as you read this you shall need to get a command terminal onto your desktop.

Also make use of your tabbed browsing in Firefox for referring to myriad notes and manuals.


Develop some Good Habits

Before testing this script, lets tidy it up in a formal sort of way; i.e., let's develop some good habits.

So the script now looks like:

#!/bin/bash
case $1 in
"") echo "[log comment]" ;;
-v|v) echo "[view comments]" ;;
-e|e) echo "[edit comments]" ;;
-p|p) echo "[print comments]" ;;
*) echo "[display usage]" ;;
esac
exit 0

It is ready to save and be tested.


Save the Script

We can save all this in a file named 'log' by typing exactly this [your typing is shown in bold] after the prompt on your terminal, starting with 'cat 1>log' and ending with 'Ctrl-d'. Remember to press the ENTER key after every line:

user@host~$ cat 1>log
#!/bin/bash
case $1 in
"") echo "[log comment]" ;;
-v) echo "[view comments]" ;;
-e) echo "[edit comments]" ;;
-p) echo "[print comments]" ;;
*) echo "[display usage]" ;;
esac
exit 0
Ctrl-d

When you see the prompt return after the Ctrl-d, check that the file has been created and contains what you typed in, by using the cat command by typing this:

user@host~$ cat file

which should produce this on the screen:

#!/bin/bash
case $1 in
"") echo "[log comment]" ;;
-v) echo "[view comments]" ;;
-e) echo "[edit comments]" ;;
-p) echo "[print comments]" ;;
*) echo "[display usage]" ;;
esac
exit 0

If it does not look like that, then use your favourite editor (e.g.., the nano editor) invoking it like this:

user@host~$ nano log

and edit the commands and fix it up until it is correct. Remember that some spaces here and there do not usually matter; they just make it more readable.

You can cut and paste the text, too, onto your terminal using the mouse, if you do not want to type it all now.


Design and Test the Top Level

Now try to run the command like this, and note the response:

user@host~$ log
bash: log: command not found

This means that bash does not know where the script is. So we need to tell bash where this script is. We can use the alias command; type this after the prompt:

user@host~$ alias log='$HOME/log'>>$HOME/.bashrc

which establishes that when we type log, bash runs the program $HOME/log. By adding this alias to the file .bashrc in your $HOME directory (>>$HOME/.bashrc) we ensure that it is established every time bash restarts.

Now try to run it again, and note the reponse:

user@host~$ log
bash: ./log: Permission denied

This means that the script is not executable (this is the default when a file is created); change this using the chmod command:

user@host~$ chmod +x log

Now at last you can type these four commands to check each part of your script, and note the responses:

user@host~$ log
[log comment]

user@host~$ log -v
[view comments]

user@host~$ log -e
[edit comments]

user@host~$ log -p
[print comments]

user@host~$ log ?
[display usage]

user@host~$

If any of the above do not work, use your favourite editor (e.g.., nano) like this:

user@host~$ nano log

and edit the log command and fix it up until the above all work.

Going well; the top level works and shall never have to be debugged again. We can now concentrate on getting the four subscripts to work.


4. Write and Test the Sub Scripts

There are four things that we have not implemented that we now have to analyse by the same top down methods.

Let's do them in order.


Log Comment

Can we do this now in bash? Not yet; there is no single command to do this. So we need further analysis. To log a comment (by typing 'log') we might need to do a number of things.

  1. tell bash where the logged comments go
  2. prompt for a comment
  3. read the comment
  4. date stamp it
  5. add it to our existing comments
  6. check that it was added OK
  7. remind ourselves of where the comments are

Can we do these in bash as they stand?

Log Comment: tell bash where the logged comments go

Yes, we can use the variable assignment statement =, and supposing that we shall put them all in a file named 'logged.comments' in our home directory, we define the variable logfile using the command (try this yourself):

user@host~$ logfile=$HOME/logged.comments

Log Comment: prompt for a comment

Yes, we can use the echo command (here is the echo manual) to put out a prompt for a comment, like this (type this yourself):

user@host~$ echo "log text: < "
user@host~$ log text: <

Log Comment: read the comment

Yes, the read command builtin to bash does that; in fact, it does more: using the '-p' option we can specify our prompt string, too, and get what we type into the variable 'comment' (try this):

user@host~$ read -p "log text: < " comment
log text: < this is a test comment
user@host~$ echo $comment
this is a test comment

Log Comment: date stamp the comment

This shall need a bit of work, but yes, we have the date program (here is the date manual) for that. Try it; it is in the /bin directory:

user@host~$ /bin/date
Sat Jul 26 15:11:35 CST 2008

or perhaps you prefer the date in another form, using its '-R' option:

user@host~$ /bin/date -R
Sat, 26 Jul 15:11:35 CST 2008

That is better. But get rid of the comma that crept in after 'Sat'. Pipe (|) the date string into the translate program with its delete (-d) option zapping the comma (','). Try it now (use the up arrow to get the last command back):

user@host~$ /bin/date -R|tr -d ','
Sat 26 Jul 15:11:35 CST 2008

We might be happy with the date part (Sat 26 Jul) but not all that time stuff, so finally let's use the awk program (here is the awk manual) to select only those first three fields ("Sat" "26" "Jul") by piping that text into the awk program [use the up arrow and then add the pipe]:

user@host~$ /bin/date -R|tr -d ','|awk '{print $1,$2,$3}'
Sat 26 Jul

Got the date stamp where we want it!

Now store that datestamp string in a variable named 'day' by a process known as command substitution. Your command should look like this:

user@host~$ $(/bin/date -R|tr -d ','|awk '{print $1,$2,$3}')

and assign that result to a variable named 'day', and then check that it is there correctly:

user@host~$ day=$(/bin/date -R|tr -d ','|awk '{print $1,$2,$3}')
user@host~$ echo $day
Sat 26 Jul

Getting somewhere now!!

Log Comment: add it to our existing comments

Yes, we can do that with the append (>>) operator (here is the append manual). We first concatenate the datestamp text (which is in the variable 'day') and the comment text (which is in the variable 'comment') like this:

user@host~$ echo "$date: $comment"

and when we have done that we can append it to the end of the logfile like this:

user@host~$ echo "$date: $comment">>$logfile

Try it.

Log Comment: check that it was added OK

The comment should have gone on as the last line of the comment file. We can use the tail program (here is the tail manual) to look at that last line (with the '-1' option):

user@host~$ tail -1 $logfile

Log Comment: remind ourselves of where the comments are

Just print out the name of the log file, like this:

user@host~$ echo "logfile is $logfile"
logfile is /home/user/logged.comments

View Comments

We can view the comments in the file using the excellent less paging program (here is the less manual).

user@host~$ less $logfile

Edit Comments

Just invoke your favourite editor. I hesitate to introduce you to vi so better just use the simpler nano editor:

user@host~$ nano $logfile

Print Comments

Send the file containing the comments to the default printer, nicely formatted using the a2ps program to convert from ASCII to postscript (here is the a2ps manual), and then check the status of the queue using the lpq program (here is the lpq manual).

user@host~$ a2ps $logfile
[/home/user/logged.comments (plain): 2 pages on 1 sheet]
request id is hp_laserjet_1200-39 (1 file(s))
[Total: 2 pages on 1 sheet] sent to the default printer
user@host~$ lpq
hp_laserjet_1200 is ready and printing

In bash, you may separate commands with a semicolon, thus putting these commands on one line for neatness, like this:

user@host~$ a2ps $logfile; lpq

Hey! We are done already!

OK, so we have developed the elements of the script by trial and error, (by design and test, I mean!) and it is in the form you want it.

Here is a record of the complete annotated script development.

And here is the final log script ready for use.

Have fun!


LSGA Tutorial: "Develop a Log Script" is copyright © Dr Gonzo Publishing 2008