Functions keep scripts readable and testable instead of one long stream of commands.
Define a function with name() { commands; } and call it like any command: name arg1 arg2. Inside, the arguments are $1, $2, $# and "$@", just like a script; $0 still names the script.
Variables are global by default, so a function that sets i clobbers the caller's i. Declare working variables with local (it is dynamically scoped: functions called from yours see them too).
Functions "return" in two ways:
- Status:
return N(0-255), or implicitly the status of the last command. This is for success/failure, so a function likeis_evencan be used directly inif. - Data: print it to stdout and capture with
result=$(myfunc). Send logs and errors to stderr (>&2) so they do not pollute captured output.
Capturing with $( ) runs the function in a subshell, so variable changes inside are lost; if you need to set a caller's variable, use a global or a name reference (local -n ref=$1, bash 4.3+).
Define functions before calling them (bash reads scripts top to bottom). A common layout is: settings, functions, then a main "$@" call at the very end, which also keeps a half-downloaded script from running partially.
Example
log() { printf '[%s] %s\n' "$(date +%T)" "$*" >&2; }
max() {
local a=$1 b=$2 # local: invisible outside the function
if (( a > b )); then echo "$a"; else echo "$b"; fi
}
is_even() { (( $1 % 2 == 0 )); } # status of last command = result
set_var() { local -n ref=$1; ref=$2; } # nameref: write caller's variable
main() {
log "starting" # [12:00:00] starting (on stderr)
local biggest
biggest=$(max 4 9) # data comes back via stdout
echo "max=$biggest" # max=9
if is_even 10; then echo "10 is even"; fi # 10 is even
is_even 7 || echo "7 is odd" # 7 is odd
set_var color blue; echo "color=$color" # color=blue
}
main "$@"Remember
- name() { ...; } with $1, $2, "$@" for arguments
- Use local for every working variable
- return is a status; output data on stdout and capture with $( )
Watch out for
- Using
return "$value"to return data: it is a 0-255 status code, not a value.