Bash for beginners. Using functions in Bash

As your bash scripts get bigger and bigger, things can get very confusing!

You can rewrite the same pieces of code over and over again in different parts of your bash scripts.

Fortunately, you can avoid rewriting your code by using functions in bash that make your scripts more organized and readable.

In this article, you will learn how to create functions, return function values, and pass function arguments in bash shell scripts.

In addition, you will learn how variable scoping works and how to define recursive functions.

Creating functions in bash

There are two different syntaxes for declaring bash functions. The following syntax is the most commonly used way to create bash functions:

function_name () {
commands
}

The second less commonly used function for creating bash functions starts with a reserved work function followed by the function name, as shown below:

function function_name {
commands
}

Now, there are a couple of things you should be aware of when working with functions:

  • The function will never run / execute unless you call the function.
  • The function definition must precede any function calls.

Every time you want a function to run, you just need to call it! To call a function, simply specify the name of the function.

Take a look at the following bash fun.sh script:

#!/bin/bash

hello () {
echo "Hello World"
}

hello
hello
hello

We have defined a function called hello that simply prints the string “Hello World” to the terminal. Note that we have made three calls to the hello function, so if you run the script, you will see the line “Hello World” printed three times on the screen:

[email protected]:~$ ./fun.sh
Hello World
Hello World
Hello World

Returning function values ​​in bash

In many programming languages, functions return a value when called; however, this is not the case with bash as bash functions do not return values.

When bash finishes executing, it returns the exit status of the last command executed, written in $? variable. Zero indicates success, or a nonzero positive integer (1-255) indicates failure.

You can use the return statement to change the exit status of a function. For example, take a look at the following error.sh script:

#! /bin/bash

error () {
blabla
return 0
}

error
echo "Состояние возвращаемого значения ошибки функции: $?"

If you run the error.sh bash script, you may be surprised at the result:

[email protected]:~$ ./error.sh
./error.sh: line 4: blabla: command not found
Состояние возвращаемого значения ошибки функции: 0

Without the return 0 statement, the error function would never have returned a nonzero exit status, because blabla results in a command not found error.

So, as you can see, although bash functions do not return values, we found a workaround by changing the exit statuses of functions.

You should also be aware that the return statement terminates the function immediately.

Passing arguments to a bash function

You can pass arguments to a function in the same way you can pass arguments to a bash script. You just include the arguments when you call the function.

To demonstrate, let’s take a look at the following bash script iseven.sh:

#!/bin/bash

iseven () {
if [ $(($1 % 2)) -eq 0 ]; then
echo "$1 is even."
else
echo "$1 is odd."
fi
}

iseven 3
iseven 4
iseven 20
iseven 111

The iseven () function checks if a number is even or odd. We’ve made four function calls to iseven (). For each function call, one number is provided, which is the first addition to the iseven () function and is referenced by the variable $ 1 in the function definition.

Let’s run the iseven.sh bash script to make sure it works:

[email protected]:~$ ./iseven.sh
3 is odd.
4 is even.
20 is even.
111 is odd.

You should also understand that bash function arguments and bash script arguments are two different things. To contrast the difference, take a look at the following bash funarg.sh script:

#!/bin/bash

fun () {
echo "$1 is the first argument to fun()"
echo "$2 is the second argument to fun()"
}

echo "$1 это первый аргумент к сценарию."
echo "$2 это второй аргумент к сценарию."

fun Yes 7

Run the script with a couple of arguments and see the result:

[email protected]:~$ ./funarg.sh Cool Stuff
Cool это первый аргумент к сценарию.
Stuff это второй аргумент к сценарию.
Yes is the first argument to fun()7 is the second argument to fun()

As you can see, even if you used the same $ 1 and $ 2 variables to refer to both script arguments and function arguments, they give different results when called from within the function.

Local and global variables in bash functions

Bash variables can be globally or locally scoped. You can access the global variable anywhere in your bash script regardless of scope. In contrast, a local variable can only be accessed from the definition of their function.

To demonstrate this, take a look at the following bash scope.sh script:

#!/bin/bash

v1='A'
v2='B'

myfun() {
local v1='C'
v2='D'
echo "Inside myfun(): v1: $v1, v2: $v2"
}

echo "Before calling myfun(): v1: $v1, v2: $v2"
myfun
echo "After calling myfun(): v1: $v1, v2: $v2"

First, we defined two global variables v1 and v2. Then, inside the definition of myfun (), we used the local keyword to define the local variable v1 and changed the global variable v2. Note that you can use the same variable name for local variables in different functions.

Now let’s run the script:

[email protected]:~$ ./scope.sh
Before calling myfun(): v1: A, v2: B
Inside myfun(): v1: C, v2: D
After calling myfun(): v1: A, v2: D

The following conclusions can be drawn from the script output:

  • A local variable with the same name as a global variable will take precedence over global variables inside the function body.
  • You can change the global variable inside the function.

Recursive functions

A recursive function is a function that calls itself! Recursive functions come in handy when you are trying to solve a programming problem that can be broken down into smaller sub-problems.

A factorial function is a classic example of a recursive function. Take a look at the following bash factorial.sh script:

#!/bin/bash

factorial () {
if [ $1 -le 1 ]; then
echo 1
else
last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))
fi
}

echo -n "4! is: "
factorial 4
echo -n "5! is: "
factorial 5
echo -n "6! is: "
factorial 6

Any recursive function must start with a base case, which must end the chain of recursive function calls. In the factorial () function, the base case is defined as follows:

if [ $1 -le 1 ]; then
echo 1

Now output the recursive case for the factorial function. To calculate the factorial of a number n, where n is a positive number greater than one, you can multiply n by the factorial n-1:

factorial(n) = n * factorial(n-1)

Let’s use the above equation to write this recursive case:

last=$(factorial $(( $1 -1)))
echo $(( $1 * last ))

Now run the script and make sure you get the correct results:

[email protected]:~$ ./factorial.sh
4! is: 24
5! is: 120
6! is: 720

As an additional exercise, try writing a recursive function to calculate the nth Fibonacci number. Try to come up with a base case first and then a recursive one.

Awesome! Hope you enjoyed creating functions in bash!

Sidebar