<<< Back to Tips Index

29 May 2015

Simple Shell Functions

One of the steps along the journey from writing the occasional shell script to being a proficient shell scripter, is the creation of custom functions to create your own toolset. Here is a small but useful example of such a thing. If you want to run a set of tests (on how a system has been configured, or how well a service is running, or anything at all) then an info() function can be very useful to standardize the way in which this is reported.

This simple script runs a set of tests, and produces a formatted output to different files - pass.log.TIMESTAMP and fail.log.TIMESTAMP. Anything not clearly a pass or a fail is logged to unknown.log.TIMESTAMP.

It starts off by defining $TIMESTAMP once, then defines the PASS, FAIL and UNKNOWN log filenames accordingly. It then defines the info() function.

First off, info() grabs the first argument passed to it, and stores it in a local variable called $STATUS. Declaring $STATUS as local ensures that it doesn't interfere with any variable called $STATUS elsewhere in your script, and allows you to save the value. The next command, shift, shifts the arguments up by one, so the status is lost from $1 (that's why we saved it in $STATUS), and $@ is all arguments left.

Then the function goes into a pretty straight-forward case construct. If $STATUS is one of the "good" words we are looking for, then the test has passed. This will be written to the output, as well as appended (via tee -a) to the $PASS_LOG file. Note that if $STATUS is zero, this pattern is matched, and no other case options are considered.

This means that any number other than zero (even if it starts with zero) is a failure, as is "fail" or "FAIL". That's what "[0-9]|FAIL|fail)" matches. The second case deals with failures.

The third and final case is "*", which matches anything else. This will be logged to the $UNKNOWN_LOG file.

After that, it's just a question of writing your tests, then calling the info() script with its status and a comment about what you were testing.

Download info.sh script
#!/bin/bash
TIMESTAMP=$(date +%d%M%Y.%H%M%S)
PASS_LOG=pass.log.${TIMESTAMP}
FAIL_LOG=fail.log.${TIMESTAMP}
UNKNOWN_LOG=unknown.log.${TIMESTAMP}

info()
{
  # Call this function with the status, followed by any comments
  local STATUS=$1 # First argument is the status
  shift           # Anything else is commentary
  case "$STATUS" in
    0|PASS|pass|ok) 
      echo "OK:${STATUS}:$@" | tee -a $PASS_LOG   ;;
    [0-9]*|FAIL|fail) 
      echo "FAIL:${STATUS}:$@" | tee -a $FAIL_LOG ;;
    *) 
      echo "Unknown status:${STATUS}:$@" | tee -a $UNKNOWN_LOG ;;
  esac
}

# Two simple tests: "true" and "false":
/bin/true  # always returns zero (i.e., success)
info $? test1 passed
/bin/false # always returns non-zero
info 1 test2 failed

# Your /etc/hosts is likely to contain "127":
grep 127 /etc/hosts > /dev/null 2>&1
info $? Searching for 127 in /etc/hosts file

# You can call info() with all sorts of results:
info pass test4 passed
info ok test5 passed
info fail test6 failed
info 0123 test 7 failed because it began with zero but is not zero
info FAIL test8 failed
info hello test9 gave a wrong status

echo
echo "RESULTS: "
[ -f $FAIL ] && echo "Some tests failed: See \"$FAIL_LOG\""
[ -f $UNKNOWN_LOG ] && echo "Some tests produced unexpected results: See \"$UNKNOWN_LOG\""

Simple functions like this, as pointless as they might seem at one level (it doesn't achieve anything much in itself), are a significant part of becoming an efficient and effective shell scripter. Without functions at all, shell scripts are just a sequence of statements, possibly with some if / then / else logic. By creating your own functions which define how the rest of the script works, you are configuring your environment to suit the task at hand. No more worrying about which log file to write to, or how pass/test comparisons are made - if any of those details need to be changed, you can do it in one place and be sure that your main code is calling the info() function in a standardized way, just like any other API.

Invest in your career. Buy my Shell Scripting Tutorial today:

 

Steve's Bourne / Bash shell scripting tips
Share on Twitter Share on Facebook Share on LinkedIn Share on Identi.ca Share on StumbleUpon