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!