2.18. Functions and Calling other Scripts

Most scripts tend to be short, but even a program of 100 lines long can benefit from being broken down and organized into modules.

The Bourne family shells support simple functions for this purpose.

C shell family shells do not support separate functions within a script, but this does not mean that they cannot be modularized. A C shell script can, of course, run other scripts and these separate scripts can serve the purpose of subprograms.

Some would argue that separate scripts are more modular than functions, since a separate script is inherently available to any script that could use it, whereas a function is confined to the script that contains it.

Another advantage of using separate scripts is that they run as a separate process, so they have their own set of shell and environment variables. Hence, they do not have side-effects on the calling script. Bourne shell functions, on the other hand, can modify "global" variables and impact the subsequent behavior of other functions or the main program.

There are some functions that are unlikely to be useful in other scripts, however, and Bourne shell functions are convenient in these cases. Also, it is generally easy to convert a Bourne shell function into a separate script, so there isn't generally much to lose by using a function initially.

2.18.1. Bourne Shell Functions

A Bourne shell function is defined by simply writing a name followed by parenthesis, and a body between curly braces on the lines below:

name()
{
    commands
}
	    

We call a function the same way we run any other command.

#!/bin/sh

line()
{
    printf '-------------------------------------------------------------\n'
}

line
	    

I we pass arguments to a function, then the variables $1, $2, etc. in the function will be set to the arguments passed to the function. Otherwise, $1, $2, etc. will be the arguments to the main script.

#!/bin/sh

print_square()
{
    printf $(($1 * $1))
}

c=1
while [ $c -le 10 ]; do
    printf "%d squared = %d\n" $c `print_square c`
    c=$((c + 1))
done

The return statement can be used to return a value to the caller. The return value is received by the caller in $?, just like the exit status of any other command. This is most often used to indicate success or failure of the function.

#!/bin/sh

myfunc()
{
    if ! command1; then
        return 1
    
    if ! command2; then
        return 1
    
    return 0
}

if ! myfunc; then
    exit 1
fi

We can define local variables in a function if we do not want the function to modify a variable outside itself.

#!/bin/sh

pause()
{
    local response
    
    printf "Press return to continue..."
    read response
}

pause

2.18.2. C Shell Separate Scripts

Since C shell does not support internal functions, we implement subprograms as separate scripts.

Each script is executed by a separate shell process, so all variables are essentially local to that script.

We can, of course, use the source to run another script using the parent shell process as described in Section 2.7, “Sourcing Scripts”. In this case, it will affect the shell and environment variables of the calling script. This is usually what we intend and the very reason for using the source command.

When using separate scripts as subprograms, it is especially helpful to place the scripts in a directory that is in your PATH. Most users use a directory such as ~/bin or ~/scripts for this purpose.

2.18.3. Self-test