In Bash shell scripting, functions are ways to group the set of instructions together to get a specific outcome. You can think of functions as a mini script. Functions are also called procedures and methods in some programming languages. Functions are a great way to achieve modularity and reusability.
In this article, I will explain how to use functions in bash scripts in Linux with examples. You will be pretty comfortable in using bash functions by the end of this article.
Table of Contents
How to define Functions in Bash
There are two important things you have to understand when working with functions.
- Defining the function,
- Calling the function.
Similar to how you create bash scripts and run them, you should define the function and call it for the function to run.
There are two syntactical ways to define a function in bash. The first way is to use the bash built-in keyword "function"
followed by the name of the function. The block of code will be written inside curly braces {}
.
function [name] {
Block of code
}
The second way is to create a function without the keyword "function"
. Start with the function name followed by brackets.
[name](){
Block of Code
}
Which one to choose? Well, it is always a personal choice. Choosing one over the other has no drawbacks.
You can also write single-line functions which are called compact functions. In compact functions, each line of code inside the braces is separated with a semicolon (;
).
Launch your terminal and write a piece of multiline function. Now press the up arrow key and you will see whatever you wrote in multi-line will be converted as compact functions.
[name](){ first_line; second_line; }
BEST PRACTICE:
- Try to choose any one syntax and be consistent with it.
- If you are working in a collaborative environment, it is important that everyone maintains the same standard when coding.
Naming convention
When you create a function you have to give a name to your function. The function name should be descriptive and try to avoid names that are already used by other functions, variables, constants, etc. "Snake Case" is the preferred way to name the function. In the snake case, the words are separated by underscores.
Take a look at the below example. I created a function named "hello_world"
in snake case style which simply will print out hello world to stdout (Terminal).
hello_world() {
echo "Running Simple Hello World Function"
}
hello_world
How to call a Function in Bash
Letβs create a simple cleanup function named "log_cleanup"
. The purpose of this function is to remove ".log"
files that are older than 30 days.
log_cleanup(){ echo "Running Cleanup On Older Logs - 30 days" find /home/karthick/Documents/Projects/logs/ -name "*.log" -type f -mtime +30 -delete echo "Cleanup Completed" }
The function is defined but will that be enough for the function to do its job? Absolutely not. You have to call the function for the function to be executed.
To call the function, simply type the function name after the function definition:
#!/usr/bin/env bash #### FUNCTION DEFINITION #### log_cleanup(){ echo "Running Cleanup On Older Logs - 30 days" find /home/karthick/Documents/Projects/logs -name "*.log" -type f -mtime +30 -delete echo "Cleanup Completed" } # Calling the function log_cleanup
If you try to call the function before the function definition you will get the following error.
line 3: log_cleanup: command not found
Why so? When you run the script, the code will be interpreted line by line from top to bottom. It will read the function and load it to the bash environment (memory). But here you are calling the function before the interpreter even reads and loads the function to its environment.
When you call a function from inside another function, your function definition can be in any order except the first function. Take a look at the below image. Function func2 is called from func1 and func3 is called from func2 before their definition. But func1 is defined first and then called. By the time func1 is called all the function definitions are already Interpreted and loaded to the environment.
Exit status & Return value
Every Linux command returns an exit status (0-255). Zero is said to be successful and the rest of the exit codes are said to be failures with different meanings. Similarly, when you run a function it also returns an exit status of the last run command in the function.
Let me run the same "cleanup" function again. But I have given a path that is not available in my machine, so the find
command will fail. I am using $?
to get the exit status within the script as shown in the image.
Running Cleanup On Older Logs - 30 days find: '/home/karthick/Documents/Projectss/logs': No such file or directory Cleanup Completed Exit status of function log_cleanup is β 0
The exit status returned from the function is from the echo
command which ran as the last command inside the function. This is not the behavior you might expect.
To overcome this behavior you can use the bash built-in "return"
statement.
$ type -a return
return is a shell builtin
Return accepts an integer [N] value and it exits the functions and gives the return value to its caller (function). Before using the return statement, you have to understand a few rules on how to use the return statement. As stated earlier, return accepts integer values from 0-255. A return statement will use the exit status of the last ran command if no argument (Integer value) is passed or the value exceeds 255.
Let me use the return to fix the "cleanup"
function behavior. Here I am using conditional statements along with the return command.
#!/usr/bin/env bash #### FUNCTION DEFINITION #### log_cleanup(){ echo "[ INFO ] - Running Cleanup On Older Logs - 30 days" if [[ -d "/home/karthick/Documents/Projectss/logs" ]] then find -name "*.log" -type f -mtime +30 -delete echo "[ SUCCESS] - Cleanup Completed" else echo "[ ERROR ] - Directory path wrong... Cleanup has not happened..." return 1 fi } # Calling the function log_cleanup echo "++ Exit status of log_cleanup function is ==> $?"
Take a look at the output below. The function is returning exit code 1 from the return statement.
[ INFO ] - Running Cleanup On Older Logs - 30 days
[ ERROR ] - Directory path wrongβ¦ Cleanup has not happenedβ¦
++ Exit status of log_cleanup function is ==> 1
Passing arguments to a Function
Similar to passing arguments to your bash scripts, functions accept arguments too. The confusing part is, functions use the same $1
β¦$9
special variables to access arguments which is the same as passing the argument to the script. You have to understand what happens when you use this special variable inside and outside the function.
cat > arg_test.sh #!/bin/bash echo "Value passed in \$1 is = $1" howdy(){ echo "value of \$1 inside function is = $1" } howdy # Function Call
Copy and run the above snippet to see the difference. The string "Howdy"
is passed as the first argument to the script.
$ ./arg_test.sh howdy
Value passed in $1 is = howdy
value of $1 inside function is =
From the output, you can see $1
inside the function is printing an empty value because $1
inside the function is different from $1
outside the function though they share the same name.
To pass arguments to the function, after the function name leave a space and pass your arguments like as shown in the below image. Each argument separated by space will be assigned with its respective variable $1
β¦$N
and you can use this variable inside the function to process the arguments.
log_cleanup $1 $2 β¦.. $N
As you see in the above screenshot, I am passing the directory name and number of days as arguments.
Variable scope for a Function
When you create a variable inside the function or outside the function, it can be accessed globally. By default, variables are created in the global scope.
Take a look at the below example. If you try to access both the variable outside_function
and inside_function
, their values are accessible. This means even though the function ran and exited, the variable created inside the function is accessible globally.
#!/bin/bash outside_function="This variable is from outside the function" func1(){ inside_function="This variable is from inside the func1" } func1 echo $outside_function echo $inside_function
In some programming languages, this might not be the behavior and the variables created inside the function will be available during the run time of the function. But in bash, the behavior is different.
To make variables local to the function you can use the bash built-in "local"
keyword. The local keyword will restrict the variable scope from global to local and the variable can only be accessed during the function run time.
#!/bin/bash
outside_function="This variable is from outside the function"
func1(){
local inside_function="This variable is from inside the func1"
}
func1
echo $outside_function
echo $inside_function
When the local keyword is used, you can use the same variable names in different functions.
Heads Up: You should always try to avoid using identifiers that are already used as variables, functions, bash keywords. The above example is shown just to understand the behavior.
Modularity and Resubaility
Understanding and writing functions can be done in quick time. But writing good functions takes time with a better understanding of the environment. As already pointed out in the introduction section, with bash functions you can achieve a great deal of modularity and reusability.
Letβs take an example. You have created 20 scripts and in each script, you have included the log_cleanup
function which we have seen in previous sections to perform housekeeping tasks. Instead of including this function in all 20 scripts you can create a single function definition and import it into 20 scripts. In this way, you are achieving modularity as well as reusable functions. This is similar to import statements in python, include statements in C, etc.
Have a look at the below image. I have created two scripts named script1.sh
and script2.sh
and the log_cleanup
function is written to a separate file named cleanup.sh
.
I am importing the function by running the source
command. The source
command will run the file passed as its argument and load the variable or functions to the current bash session. This way when you run log_cleanup
from another script file the function is already loaded to the current environment and accessible.
From the above image, you can understand how the arguments are helpful. There is only one function definition and according to the use case, I can modify the function to accept different arguments and in different scripts.
Heads Up: You can also run your shell scripts with source
command which will run the script in the current shell instead of creating a subshell to run the script.
Conclusion
In this guide, we have discussed about Bash functions and how to define and call a function in scripts with examples. To get comfortable with functions, you have to practice the functions with different use cases. If you have any questions or feedback, feel free to let us know via the comment section below.
Related read:
- Bash Scripting β For Loop Explained With Examples
- Bash Scripting β While And Until Loop Explained With Examples
- Difference Between Defining Bash Variables With And Without export
- Bash Echo Command Explained With Examples In Linux
- Bash Heredoc Tutorial For Beginners
- Bash Redirection Explained With Examples
1 comment
Very nice post! Thank you for sharing! π