Functions are the key to writing just about ANY program that is longer than a page or so of text. Other languages may call functions something else. But essentially, its all a matter of breaking up a large program, into smaller, managable chunks. Ideally, functions are sort of like 'objects' for program flow. You pick a part of your program that is pretty much self-contained, and make it into its own 'function'
Properly written functions can exist by themselves, and affect only small things external to themselves. You should DOCUMENT what things it changes external to itself. Then you can look very carefully just at the function, and determine whether it actually does what you think it does :-)
When your program isn't working properly(WHEN, not if), you can then put in little debug notes to yourself in the approximate section you think is broken. If you suspect a function is not working, then all you have to verify is
Once you have done that, you then know the entire function is correct, for that particular set of input(s), and you can look for errors elsewhere.
printmessage() { echo "Hello, this is the printmessage function" } printmessage
The first part, from the first "printmessage()" all the way through the final '}', is the function definition. It only defines what the function does, when you decide to call it. It does not DO anything, until you actually say "I want to call this function now".
You call a function in ksh, by pretending it is a regular command, as shown above. Just have the function name as the first part of your line. Or any other place commands go. For example,
echo The message is: `printmessage`
Remember: Just like its own separate shellscript. Which means if you access "$1" in a function, it is the first argument passed in to the function, not the shellscript.
If you are really really having difficulties, it should be easy to copy the entire function into another file, and test it separately from the main program.
This same type of modularity can be achived by making separate script files, instead of functions. In some ways, that is almost preferable, because it is then easier to test each part by itself. But functions run much faster than separate shellscripts.
A nice way to start a large project is to start with multiple, separate shellscripts, but then encapsulate them into functions in your main script, once you are happy with how they work.
THE main difference when changing between shellscripts and functions, is the use of "exit".
'exit' will exit the entire script, whether it is in a function or not.
'return' will just quit the function. Like 'exit', however, it can return
the default "sucess" value of 0, or any number from 1-255 that you specify.
You can then check the return value of a function, just in the same way you
can check the return value of an external program, with the $?
variable.
# This is just a dummy script. It does not DO anything fatal(){ echo FATAL ERROR # This will quit the 'fatal' function, and the entire script that # it is in! exit } lessthanfour(){ if [[ "$1" = "" ]] ; then echo "hey, give me an argument" ; return 1; fi # we should use 'else' here, but this is just a demonstration if [[ $1 -lt 4 ]] ; then echo Argument is less than 4 # We are DONE with this function. Dont do anything else in # here. But the shellscript will continue at the caller return fi echo Argument is equal to or GREATER than 4 echo We could do other stuff if we wanted to now } echo note that the above functions are not even called. They are just echo defined
A bare "return" in a shellscript is an error. It can only be used inside a function.
To avoid this behaviour, and give what is known as "local scope" to a variable, you can use the#!/bin/sh # Acts the same with /bin/sh, or /bin/ksh, or /bin/bash subfunc(){ echo sub: var starts as $var var=2 echo sub: var is now $var } var=1 echo var starts as $var, before calling function '"subfunc"' subfunc # calls the function echo var after function is now $var
typeset
command, to define a
variable as local to the function.
Another exception to this is if you call a function in the 'background', or as part of a pipe (like#!/bin/ksh # You must use a modern sh like /bin/ksh, or /bin/bash for this subfunc(){ typeset var echo sub: var starts as $var '(empty)' var=2 echo sub: var is now $var } var=1 echo var starts as $var, before calling function '"subfunc"' subfunc # calls the function echo var after function is now $var
echo val | function
)func() { newval=$(($1 + 1)) echo $newval echo in func: newval ends as $newval } newval=1 echo newval in main is $newval output=`func $newval` func $newval echo output is : $output echo newval finishes in main as $newval
KSH93 (or later) is optimized to not fork a new process for functions.
Sadly, earlier versions do call fork().
So, if you are writing particularly resource-sensitive largescale
shellscripts, be sure to use ksh93 or newer.