Functions, Child Scripts, and Aliases

Most shell 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 and is prone to get reinvented.

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 variables in the main script and other functions, impacting the subsequent behavior in ways that will be difficult to debug.

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 -e

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

line
            

If 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 command-line arguments passed to the script.

#!/bin/sh -e

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 command can be used to return a value to the caller, much like exit returns a value from the main script. The return value is received by the caller in $?, just like the exit status of any other command.

#!/bin/sh -e

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

if ! myfunc; then
    exit 1
fi
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 child process, so all variables are local to that process.

We can, of course, use the source to run another script using the parent shell process as described in the section called “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.

Aliases

An alternative to functions and separate scripts for very simple things is the alias command. This command creates an alias, or alternate name for another command. Aliases are supported by both Bourne and C shell families, albeit with a slightly different syntax. Aliases are most often used to create simple shortcuts for common commands.

In Bourne shell and derivatives, the new alias is followed by an '='. Any command containing white space must be enclosed in quotes, or the white space must be escaped with a '\'.

#!/bin/sh -e

alias dir='ls -als'

dir
            

C shell family shells use white space instead of an '=' and do not require quotes around commands containing white space.

#!/bin/csh -ef

alias dir ls -als

dir
            

An alias can contain multiple commands separated by semicolons, but in this case it must be enclosed in quotes, even in C shell, since the semicolon would otherwise indicate the end of the alias command.

#!/bin/csh -ef

# This will not work:
# alias pause printf "Press return to continue..."; $<
#
# It is the same as:
#
# alias pause printf "Press return to continue..."
# $<

# This works
alias pause 'printf "Press return to continue..."; $<'

pause
            
Practice

Note

Be sure to thoroughly review the instructions in Section 2, “Practice Problem Instructions” before doing the practice problems below.
  1. What is the advantage of using a Bourne shell function as opposed to running a separate script?

  2. What are the advantages of using a separate script, as opposed to a Bourne shell function?

  3. Show how to create an alias called rls which runs find . -type f in Bourne shell and C shell.