Advanced Bash-Scripting Guide:
Prev |  | Next

# Chapter 24. Functions

Like "real" programming languages, Bash has functions, though in a somewhat limited implementation. A function is a subroutine, a code block that implements a set of operations, a "black box" that performs a specified task. Wherever there is repetitive code, when a task repeats with only slight variations in procedure, then consider using a function.

function `function_name` {
`command`...
}

or

`function_name` () {
`command`...
}

This second form will cheer the hearts of C programmers (and is more portable).

As in C, the function's opening bracket may optionally appear on the second line.

`function_name` ()
{
`command`...
}

| A function may be "compacted" into a single line.
    fun () { echo "This is a function"; echo; }
    #                                 ^     ^
In this case, however, a semicolon must follow the final command in the function.
    fun () { echo "This is a function"; echo } # Error!
    #                                       ^

    fun2 () { echo "Even a single-command function? Yes!"; }
    #                                                    ^

Functions are called, triggered, simply by invoking their names. A function call is equivalent to a command.

Example 24-1. Simple functions

    #!/bin/bash

    JUST_A_SECOND=1

    funky ()
    { # This is about as simple as functions get.
      echo "This is a funky function."
      echo "Now exiting funky function."
    } # Function declaration must precede call.

    fun ()
    { # A somewhat more complex function.
      i=0
      REPEATS=30

      echo
      echo "And now the fun really begins."
      echo

      sleep $JUST_A_SECOND    # Hey, wait a second!
      while [ $i -lt $REPEATS ]
      do
        echo "----------FUNCTIONS---------->"
        echo "<------------ARE-------------"
        echo "<------------FUN------------>"
        echo
        let "i+=1"
      done
    }

      # Now, call the functions.

    funky
    fun

    exit 0

The function definition must precede the first call to it. There is no method of "declaring" the function, as, for example, in C.

    f1
    # Will give an error message, since function "f1" not yet defined.

    declare -f f1      # This doesn't help either.
    f1                 # Still an error message.

    # However...

    f1 ()
    {
      echo "Calling function \"f2\" from within function \"f1\"."
      f2
    }

    f2 ()
    {
      echo "Function \"f2\"."
    }

    f1  #  Function "f2" is not actually called until this point,
        #+ although it is referenced before its definition.
        #  This is permissible.

        # Thanks, S.C.

| Functions may not be empty!
    #!/bin/bash
    # empty-function.sh

    empty ()
    {
    }

    exit 0  # Will not exit here!

    # $ sh empty-function.sh
    # empty-function.sh: line 6: syntax error near unexpected token `}'
    # empty-function.sh: line 6: `}'

    # $ echo $?
    # 2

    # Note that a function containing only comments is empty.

    func ()
    {
      # Comment 1.
      # Comment 2.
      # This is still an empty function.
      # Thank you, Mark Bova, for pointing this out.
    }
    # Results in same error message as above.

    # However ...

    not_quite_empty ()
    {
      illegal_command
    } #  A script containing this function will *not* bomb
      #+ as long as the function is not called.

    # Thank you, Thiemo Kellner, for pointing this out.

It is even possible to nest a function within another function, although this is not very useful.

    f1 ()
    {

      f2 () # nested
      {
        echo "Function \"f2\", inside \"f1\"."
      }

    }

    f2  #  Gives an error message.
        #  Even a preceding "declare -f f2" wouldn't help.

    echo

    f1  #  Does nothing, since calling "f1" does not automatically call "f2".
    f2  #  Now, it's all right to call "f2",
        #+ since its definition has been made visible by calling "f1".

        # Thanks, S.C.

Function declarations can appear in unlikely places, even where a command would otherwise go.

    ls -l | foo() { echo "foo"; }  # Permissible, but useless.

    if [ "$USER" = bozo ]
    then
      bozo_greet ()   # Function definition embedded in an if/then construct.
      {
        echo "Hello, Bozo."
      }
    fi

    bozo_greet        # Works only for Bozo, and other users get an error.

    # Something like this might be useful in some contexts.
    NO_EXIT=1   # Will enable function definition below.

    [[ $NO_EXIT -eq 1 ]] && exit() { true; }     # Function definition in an "and-list".
    # If $NO_EXIT is 1, declares "exit ()".
    # This disables the "exit" builtin by aliasing it to "true".

    exit  # Invokes "exit ()" function, not "exit" builtin.

    # Or, similarly:
    filename=file1

    [ -f "$filename" ] &&
    foo () { rm -f "$filename"; echo "File "$filename" deleted."; } ||
    foo () { echo "File "$filename" not found."; touch bar; }

    foo

    # Thanks, S.C. and Christopher Head

Function names can take strange forms.

      _(){ for i in {1..10}; do echo -n "$FUNCNAME"; done; echo; }
    # ^^^         No space between function name and parentheses.
    #             This doesn't always work. Why not?

    # Now, let's invoke the function.
      _         # __________
    #             ^^^^^^^^^^   10 underscores (10 x function name)!
    # A "naked" underscore is an acceptable function name.

    # In fact, a colon is likewise an acceptable function name.

    :(){ echo ":"; }; :

    # Of what use is this?
    # It's a devious way to obfuscate the code in a script.

See also Example A-55

| What happens when different versions of the same function appear in a script?
    #  As Yan Chen points out,
    #  when a function is defined multiple times,
    #  the final version is what is invoked.
    #  This is not, however, particularly useful.

    func ()
    {
      echo "First version of func ()."
    }

    func ()
    {
      echo "Second version of func ()."
    }

    func   # Second version of func ().

    exit $?

    #  It is even possible to use functions to override
    #+ or preempt system commands.
    #  Of course, this is *not* advisable.

Prev | Home | Next
Process Substitution | Up | Complex Functions and Function Complexities