This a translation of a post from 2013 with some edits, but still relevant for learning BASH.
In fact, a function in bash is a regular variable, but with more features.
The main use is when the same code needs to be used several times and/or in different related scripts.
Contents
Declaring and calling a function
The function is declared like this:
function function_name ()
{
function body
}
Or:
function one {
echo "One"
}
two () {
echo "Two"
}
function three () {
echo "Three"
}
However, the most correct option, in order to make the script compatible with different shell versions, would be the second one:
two () {
echo "Two"
}
And try to never use the third option:
function three () {
echo "Three"
}
You can call a function simply by specifying its name in the body of the script:
#!/bin/bash
function one {
echo "One"
}
one
[simterm]
$ ./example.sh One
[/simterm]
It is important that the function declaration be exist before it is called, otherwise, an error will be received:
#!/bin/bash
function one {
echo "One"
}
one
two
function two {
echo "Two"
}
[simterm]
$ ./example.sh One ./example.sh: line 7: two: command not found
[/simterm]
Calling a function with arguments
Let’s move on to a more complex function, and consider calling a function with arguments.
For example, let’s take a function that is called at the place in the code where you need to get a response from the user:
#!/bin/bash
answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1"
$2
break
;;
[nN][oO]|[nN])
printf "$3"
$4
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}
echo "Run application? (Yes/No) "
answer "Run" "" "Not run" ""
In this case, the function answer() expects a response from the user in the style Yesor No(or any variation given in the expression [yY][eE][sS]|[yY]or [nN][oO]|[nN]), and depending on the response, performs a certain action.
In case of a response Yes, the action specified in the first argument $1 with which the function was called will be performed.
Let’s check:
[simterm]
$ bash test.sh Run application? (Yes/No) y Run
[/simterm]
With answer No:
[simterm]
$ ./example.sh Run application? (Yes/No) no Not run
[/simterm]
Calling commands directly from arguments, and even more so from variables, is considered not the best solution, so let’s rewrite it and call it with the operators && (that is in case of success, when receiving code 0) and || – in case of an error and receiving response code 1:
#!/bin/bash
answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1\n"
return 0
break
;;
[nN][oO]|[nN])
printf "$2\n"
return 1
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}
echo -e "\nRun application? (Yes/No) "
answer "Run" "Will not run" && echo "I'm script" || echo "Doing nothing"
Now we pass the answer “Run” as the first argument to the function, and in the case of the user’s answer “Yes“, we’ll execute the printf "Run" and echo "I'm script". If the answer Nois selected, then we print the second argument Will not run, and perform the action echo "Doing nothing":
[simterm]
$ bash test.sh Run application? (Yes/No) y Run I'm script $ bash test.sh Run application? (Yes/No) no Will not run Doing nothing
[/simterm]
Accordingly, instead of the echo you can run any other command:
#!/bin/bash
answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1\n"
return 0
break
;;
[nN][oO]|[nN])
printf "$2\n"
return 1
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}
echo -e "\nKill TOP application? (Yes/No) "
answer "Killing TOP" "Left it alive" && pkill top || echo "Doing nothing"
[simterm]
$ ./example.sh Kill TOP application? (Yes/No) y Killing TOP
[/simterm]
It is important to keep in mind that if the first command fails (in this example, pkill does not find the specified process), then the function will return code 1, and the second part will be executed:
[simterm]
$ ./example.sh Kill TOP application? (Yes/No) y Killing TOP Doing nothing
Variables in functions
Variables can also be used in arguments.
For example, you can define several answers in different variables, and use the right one in different cases:
#!/bin/bash
answer () {
while read response; do
echo
case $response in
[yY][eE][sS]|[yY])
printf "$1\n"
return 0
break
;;
[nN][oO]|[nN])
printf "$2\n"
return 1
break
;;
*)
printf "Please, enter Y(yes) or N(no)! "
esac
done
}
replay1="Killing TOP"
replay2="Left it alive"
echo -e "\nKill TOP application? (Yes/No) "
answer "$replay1" "$replay2" && echo "I'm script" || echo "Doing nothing"
[simterm]
$ ./example.sh Kill TOP application? (Yes/No) y Killing TOP I'm script
$ ./example.sh Kill TOP application? (Yes/No) n Left it alive Doing nothing
[/simterm]
As with regular variables, functions use “positional arguments”, i.e.:
$#– display the number of passed arguments$*– display a list of all passed arguments$@– the same as$*– but each argument is considered as a simple word (string)$1 - $9– are numbered arguments, depending on the position in the list
For example, let’s create a script with a function that should display the number of arguments passed:
#!/bin/bash
example () {
echo $#
shift
}
example $*
[simterm]
$ ./example.sh 1 2 3 4 4
[/simterm]
Or just display all the arguments passed to it:
#!/bin/bash
example () {
echo $*
shift
}
example $*
[simterm]
$ ./example.sh 1 2 3 4 1 2 3 4
[/simterm]
Or you can pass arguments directly when calling a function, and not when calling a script, as in the example above:
#!/bin/bash
example () {
echo $*
shift
}
example 1 2 3 4
[simterm]
$ ./example.sh 1 2 3 4
[/simterm]
Local variables
By default, all given variables in bash scripts are considered global within the script itself, but in a function, you can declare a local variable that will be available only during its (function) execution.
Example:
#!/bin/bash
ex0=0
example () {
local ex1=1
echo "$ex1"
}
example
[[ $ex0 ]] && echo "Variable found" || echo "Can't find variable!"
[[ $ex1 ]] && echo "Variable found" || echo "Can't find variable!"
Check it:
[simterm]
$ bash test.sh 1 Variable found Can't find variable!
[/simterm]
Math operations in functions
As with variables, functions can use mathematical operations.
For example this function:
#!/bin/bash
mat () {
a=1
(( a++ ))
echo $a
}
mat
As a result, we get the value of the variable $a + 1:
[simterm]
$ ./mat.sh 2
[/simterm]
A more complex example – using several variables and calculating their value:
#!/bin/bash
mat () {
a=1
b=2
c=$(( a + b ))
echo $c
}
mat
Result:
[simterm]
$ ./mat.sh 3
[/simterm]
Another option is to use arguments:
#!/bin/bash
mat () {
a=$1
b=$2
c=$(( a + b ))
echo $c
}
mat $1 $2
Run it:
[simterm]
$ ./mat.sh 1 1 2
[/simterm]
Recursive functions
A recursive function is a function that, when called, calls itself.
For example:
#!/bin/bash
recursion () {
count=$(( $count + 1 ))
echo $count
recursion
}
recursion
Such a function will call itself endlessly until its execution is manually interrupted:
[simterm]
$ ./example.sh ... 913 914 915
[/simterm]
For better clarity, let’s add a loop that checks the condition: if the variable $count exceeds the value of the variable $recursions, then the function will stop its execution:
#!/bin/bash
count=0
recursions=4
recursion () {
count=$(( $count + 1 ))
echo $count
while [ $count -le $recursions ]; do
recursion
done
}
recursion
Run the script:
[simterm]
$ ./example.sh 1 2 3 4 5
[/simterm]
To simplify the script, you can replace the expression count=$(( $count + 1 )) with (( count++ )):
#!/bin/bash
count=0
recursions=4
recursion () {
(( count++ ))
echo $count
while [ $count -le $recursions ]; do
recursion
done
}
recursion
Check it:
[simterm]
$ ./example.sh 1 2 3 4 5
[/simterm]
Export functions
To pass a function to the next script called in a new (child) instance of shell, it must be exported.
For example, let’s take two files – in the file 1.sh we will declare a function and call the script 2.sh:
#!/bin/bash
one () {
echo "one"
}
bash 2.sh
And in the file 2.sh will try to use this function:
#!/bin/bash one
Run it:
[simterm]
$ ./1.sh 2.sh: line 3: one: command not found
[/simterm]
Now, export the function using the option export with the key -f:
#!/bin/bash
one () {
echo "one"
}
export -f one
bash 2.sh
Run:
[simterm]
$ ./1.sh one
[/simterm]
Another option is to call the second script in the same instance of the shell by using the source:
#!/bin/bash
one () {
echo "one"
}
source 2.sh
Or so:
#!/bin/bash
one () {
echo "one"
}
. 2.sh
Both options are equivalent and will give the same result:
[simterm]
$ ./1.sh one
[/simterm]
Checking for a function availability
Sometimes it is necessary to check if a function exists before executing it. For this, we can use the declare command.
Called with a key -f and no arguments declare will display a bodies of all available functions:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
declare -f
Result:
[simterm]
$ ./test.sh
one ()
{
echo "one"
}
two ()
{
echo "two"
}
[/simterm]
With the key -F – only names:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
declare -F
And:
[simterm]
$ ./test.sh declare -f one declare -f two
[/simterm]
If you specify function names as arguments, declare will simply display their names:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
declare -F one two
Check it:
[simterm]
$ ./test.sh one two
[/simterm]
You can set the key -f and a name of the function, then only the body of the specified function will be displayed:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
declare -f one
Run:
[simterm]
$ ./test.sh
one () {
echo "one"
}
[/simterm]
You can check the presence of functions before executing them using an additional function, and passing names of functions to be checked:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
isDefined() {
declare -f "$@" > /dev/null && echo "Functions exist" || echo "There is no some functions!"
}
isDefined one two
Pay attention to the use of “$@” – as it was written above, it is a parameter that displays the argument “as is”, without any interpretation by bash.
Let’s run the script to check:
[simterm]
$ ./test.sh Functions exist
[/simterm]
And now, let’s try to add one “extra” function:
#!/bin/bash
one () {
echo "one"
}
two () {
echo "two"
}
isDefined() {
declare -f "$@" > /dev/null && echo "Functions exist" || echo "There is no some functions!"
}
isDefined one two three
Result:
[simterm]
$ ./test.sh There is no some functions!
[/simterm]
declare detected the absence of the function three, and returned code 1, which caused the ||.




