Previous Section  < Day Day Up >  Next Section

14.7. Functions

Functions were introduced to the Bourne shell in AT&T's UNIX SystemVR2 and have been enhanced in the Bourne Again shell. A function is a name for a command or group of commands. Functions are used to modularize your program and make it more efficient. They are executed in context of the current shell. In other words, a child process is not spawned as it is when running an executable program such as ls. You may even store functions in another file and load them into your script when you are ready to use them.

Here is a review of some of the important rules about using functions.

  1. The shell determines whether you are using an alias, a function, a built-in command, or an executable program (or script) found on the disk. It looks for aliases first, then functions, built-in commands, and executables last.

  2. A function must be defined before it is used.

  3. The function runs in the current environment; it shares variables with the script that invoked it, and lets you pass arguments by assigning them as positional parameters. Local variables can be created within a function by using the local function.

  4. If you use the exit command in a function, you exit the entire script. If you exit the function, you return to where the script left off when the function was invoked.

  5. The return statement in a function returns the exit status of the last command executed within the function or the value of the argument given.

  6. Functions can be exported to subshells with the export –f built-in command.

  7. To list functions and definitions, use the declare –f command. To list just function names, use declare –F .[5]

    [5] Only on bash versions 2.x.

  8. Traps, like variables, are global within functions. They are shared by both the script and the functions invoked in the script. If a trap is defined in a function, it is also shared by the script. This could have unwanted side effects.

  9. If functions are stored in another file, they can be loaded into the current script with the source or dot command.

  10. Functions can be recursive; that is, they can call themselves. There is no limit imposed for the number of recursive calls.

FORMAT


function function_name { commands ; commands; }


Example 14.55.

function dir { echo "Directories: ";ls -l|awk '/^d/ {print $NF}'; }


EXPLANATION

The keyword function is followed by the name of the function dir. (Sometimes empty parentheses follow the function name, but they are not necessary.) The commands within the curly braces will be executed when dir is typed. The purpose of the function is to list only the subdirectories below the present working directory. The spaces surrounding the first curly brace are required.

14.7.1 Unsetting Functions

To remove a function from memory, use the unset command.

FORMAT


unset -f function_name


14.7.2 Exporting Functions

Functions may be exported so that subshells know about them.

FORMAT


export -f function_name


14.7.3 Function Arguments and the Return Value

Because the function is executed within the current shell, the variables will be known to both the function and the shell. Any changes made to your environment in the function will also be made to the shell.

Arguments

Arguments can be passed to functions by using positional parameters. The positional parameters are private to the function; that is, arguments to the function will not affect any positional parameters used outside the function. See Example 14.56.

The Built-In local Function

To create local variables that are private to the function and will disappear after the function exits, use the built-in local function. See Example 14.57.

The Built-In return Function

The return command can be used to exit the function and return control to the program at the place where the function was invoked. (Remember, if you use exit anywhere in your script, including within a function, the script terminates.) The return value of a function is really just the value of the exit status of the last command in the script, unless you give a specific argument to the return command. If a value is assigned to the return command, that value is stored in the ? variable and can hold an integer value between 0 and 256. Because the return command is limited to returning only an integer between 0 and 256, you can use command substitution to capture the output of a function. Place the entire function in parentheses preceded by a $ (e.g., $(function_name)), or traditional backquotes to capture and assign the output to a variable just as you would if getting the output of a UNIX command.

Example 14.56.

(Passing Arguments)

(The Script)

    #!/bin/bash

    # Scriptname: checker

    # Purpose: Demonstrate function and arguments



1   function Usage { echo "error: $*" 2>&1; exit 1; }



2   if (( $# != 2 ))

    then

3        Usage "$0: requires two arguments"

    fi

4   if [[ ! ( -r $1 && -w $1 ) ]]

    then

5        Usage "$1: not readable and writable"

    fi

6   echo The arguments are: $*

 <  Program continues here >



(The Command Line and Output)

$ checker

error: checker: requires two arguments

$ checker file1 file2

error: file1: not readable and writable

$ checker filex file2

The arguments are filex file2


EXPLANATION

1 The function called Usage is defined. It will be used to send an error message to standard error (the screen). Its arguments consist of any string of characters sent when the function is called. The arguments are stored in $*, the special variable that contains all positional parameters. Within a function, positional parameters are local and have no effect on those positional parameters used outside the function.

2 If the number of arguments being passed into the script from the command line does not equal 2, the program branches to line 3.

3 When the Usage function is called, the string $0: requires two arguments is passed to the function and stored in the $* varable. The echo statement will then send the message to standard error and the script will exit with an exit status of 1 to indicate that something went wrong.[a]

[a] With the old test form, the expression is written if [ ! \( -r $1 -a -w $1 \) ].

4, 5 If the first argument coming into the program from the command line is not the name of a readable and writable file, the Usage function will be called with $1: not readable and writable as its argument.

6 The arguments coming into the script from the command line are stored in $*. This has no effect on the $* in the function.

Example 14.57.

(Using the return Command)

(The Script)

    #!/bin/bash

    # Scriptname: do_increment

1   increment ()  {

2      local sum;     # sum is known only in this function

3      let "sum=$1 + 1"

4      return $sum    # Return the value of sum to the script

    }

5   echo  –n "The sum is "

6   increment 5    # Call function increment; pass 5 as a

                   # parameter; 5 becomes $1 for the increment function

7   echo $?        # The return value is stored in $?

8   echo  $sum     # The variable "sum" is not known here



(The Output)

4,6  The sum is 6

8


EXPLANATION

  1. The function called increment is defined.

  2. The built-in local function makes variable sum local (private) to this function. It will not exist outside the function. When the function exits, it will be removed.

  3. When the function is called, the value of the first argument, $1, will be incremented by one and the result assigned to sum.

  4. The return built-in command, when given an argument, returns to the main script after the line where the function was invoked. It stores its argument in the ? variable.

  5. The string is echoed to the screen.

  6. The increment function is called with an argument of 5.

  7. When the function returns, its exit status is stored in the ? variable. The exit status is the exit value of the last command executed in the function unless an explicit argument is used in the return statement. The argument for return must be an integer between 0 and 255.

  8. Although the sum was defined in the function increment, it is local in scope, and therefore also not known outside the function. Nothing is printed.

Example 14.58.

(Using Command Substitution)

(The Script)

    #!/bin/bash

    # Scriptname: do_square

1   function square {

      local sq     # sq is local to the function

      let "sq=$1 * $1"

      echo "Number to be squared is $1."

2      echo "The result is $sq "

    }

3   echo "Give me a number to square. "

    read number

4   value_returned=$(square $number)  # Command substitution

5   echo   "$value_returned"



(The Command Line and Output)

    $ do_square

3   Give me a number to square.

    10

5   Number to be squared is 10.

    The result is 100


EXPLANATION

  1. The function called square is defined. Its purpose, when called, is to multiply its argument, $1, times itself.

  2. The result of squaring the number is printed.

  3. The user is asked for input. This is the line where the program starts executing.

  4. The function square is called with a number (input from the user) as its argument. Command substitution is performed because the function is enclosed in parentheses preceded by a $. The output of the function, both of its echo statements, is assigned to the variable value_returned.

  5. The value returned from the command substitution is printed.

14.7.4 Functions and the source (or dot) Command

Storing Functions

Functions are often defined in the .profile file so that when you log in, they will be defined. Functions can be exported, and they can be stored in a file. Then when you need the function, the source or dot command is used with the name of the file to activate the definitions of the functions within it.

Example 14.59.

1   $ cat myfunctions

2   function go() {

        cd $HOME/bin/prog

        PS1='`pwd` > '

        ls

    }

3   function greetings() { echo "Hi $1! Welcome to my world." ; }

4   $ source myfunctions

5   $ greetings george

    Hi george! Welcome to my world.


EXPLANATION

  1. The file myfunctions is displayed. It contains two function definitions.

  2. The first function defined is called go. It sets the primary prompt to the present working directory.

  3. The second function defined is called greetings. It will greet the name of the user passed in as an argument.

  4. The source or dot command loads the contents of the file myfunctions into the shell's memory. Now both functions are defined for this shell.

  5. The greetings function is invoked and executed.

Example 14.60.

(The dbfunctions file shown below contains functions to be used by the main program. See

 cd for complete script.)



1   $ cat dbfunctions

2   function addon () {     # Function defined in file dbfunctions

3       while true

        do

            echo "Adding information "

            echo "Type the full name of employee "

            read name

            echo "Type address for employee "

            read address

            echo "Type start date for employee (4/10/88 ) :"

            read startdate

            echo $name:$address:$startdate

            echo –n "Is this correct? "

            read ans

            case "$ans"  in

            [Yy]*)

                 echo "Adding info..."

                 echo $name:$address:$startdate>>datafile

                 sort –u datafile –o datafile

                 echo –n "Do you want to go back to the main menu? "

                 read ans

                 if [[ $ans == [Yy] ]]

                 then



4                return     # Return to calling program

                 else

5                continue   # Go to the top of the loop

                 fi

                 ;;

            *)

            echo "Do you want to try again? "

            read answer

            case "$answer" in

            [Yy]*) continue;;

            *) exit;;

            esac

                ;;

        esac

    done

6   }     # End of function definition



-------------------------------------------------------------

(The Command Line)

7   $ more mainprog

    #!/bin/bash

    # Scriptname: mainprog

    # This is the main script that will call the function, addon



    datafile=$HOME/bourne/datafile

8   source dbfunctions     # The file is loaded into memory

        if [ ! –e $datafile ]

    then

        echo "$(basename $datafile) does not exist" >&2

        exit 1

    fi

9       echo "Select one: "

        cat <<EOF

            [1] Add info

            [2] Delete info

            [3] Update info

            [4] Exit

        EOF

        read choice

        case $choice in

10          1)   addon      # Calling the addon function

                 ;;

            2)   delete     # Calling the delete function

                 ;;

            3)   update

                 ;;

            4)

                 echo Bye

                 exit 0

                 ;;

            *)   echo Bad choice

                 exit 2

                 ;;

        esac

        echo Returned from function call

        echo The name is $name

        # Variable set in the function are known in this shell.

    done


EXPLANATION

  1. The dbfunctions file is displayed.

  2. The addon function is defined. Its function is to add new information to datafile.

  3. A while loop is entered. It will loop forever unless a loop control statement such as break or continue is included in the body of the loop.

  4. The return command sends control back to the calling program where the function was called.

  5. Control is returned to the top of the while loop.

  6. The closing curly brace ends the function definition.

  7. This is the main script. The function addon will be used in this script.

  8. The source command loads the file dbfunctions into the program's memory. Now the function addon is defined for this script and available for use. It is as though you had just defined the function right here in the script.

  9. A menu is displayed with the here document. The user is asked to select a menu item.

  10. The addon function is invoked.

    Previous Section  < Day Day Up >  Next Section