22. Shell Functions

Bash allows reuse of a list of commands by functions. Therefore, a function has to be declared before it is first called. The declaration may start with the keyword function:

$ function greet {
  echo "Welcome ${1}, today is $(date)."
}

In absence of the keyword function brackets are needed after the function name:

$ greet() {
  echo "Welcome ${1}, today is $(date)."
}

The function body is always encapsulated by curly braces. The commands within the body are executed each time the function is called. Functions are invoked like commands:

$ greet $USER
Welcome pa, today is Tue Oct 16 17:02:08 CEST 2012.

The parameters of the function are given after the functions name. Only inside the function body the parameters are accessible through the positional parameters: $1, $2, $3, .... Beware of word splitting:

$ parameters() {
  echo '"' $1 $2 $3 '"' a film by $4
}
$ words="A Clockwork Orange"
$ parameters ${words} "Stanley Kubrick"
" A Clockwork Orange " a film by Stanley Kubrick

Its not possible to pass arrays to functions:

$ parameters() {
  echo $*;
}
$ fruits=(orange lemon apple melone banana)
$ parameters $fruits
orange

Functions declared last overwrite functions and commands with the same name:

$ function ls() {
  echo "Sorry I cover the ls command";
}
$ ls
Sorry I cover the ls command

Functions are unset like variables by the unset command:

$ unset ls

The return status (exit status) of the function declaration is 0 unless no syntax error occurs. The return status of the function is the return status of the last executed command. The return command returns from a function with a given status.

$ returns() {
  return $1;
  echo "never reached";
}
$ returns 72
$ echo $?
72

The return status can be interpreted as return value of the function. The return value is read from the special parameter $?. Local variables can be declared inside functions by the keyword local only. Local variables hide the actual value of a global variable:

$ int=0
$ function klocal() {
  local int=1
  echo $int
}
$ echo $int && klocal && echo $int
0
1
0

Functions are nestable:

$ wrapper() {
  local USER="insider"
  mylogin() {
    echo "Hello $USER"
  }
  mylogin
}
$ wrapper && mylogin
Hello insider
Hello pa

The parameter USER is covered inside wrapper but not if calling mylogin directly. So there are really no closures in Bash. Otherwise calling mylogin directly would output insider also. A function can call itself recursively:

$ recursive() {
  if (( ${1:-0} < 4 ))
  then
    recursive $((${1:-0} + 1))
  fi
  echo "${1:-0}. recursive call"  
}
$ recursive
4. recursive call
3. recursive call
2. recursive call
1. recursive call
0. recursive call

The output of a function can be redirected also:

redirect() {
 date
} > /dev/null
$ redirect