In terms of Bash scripting, we can think of a function as a script within a script, which is useful
when we need to execute the same code multiple times in a script. Rather than re-writing the same
chunk of code over and over, we just write it once as a function and then call that function as
needed.
Put another way, a function is a subroutine, or a code block that implements a set of operations–a
“black box” that performs a specified task. Functions may be written in two different formats. The
first format is more common to Bash scripts:
1 | function function_name { |
The second format is more familiar to C programmers:
1 | function_name () { |
The formats are functionally identical and are a matter of personal preference. Let’s look at a simple example:
1 | kali@kali:~$ cat ./func.sh |
Functions can also accept arguments:
1 | kali@kali:~$ cat ./funcarg.sh |
In this case, we passed a random number, $RANDOM, into the function, which outputs it as $1, the
functions first argument. Note that the function definition (pass_arg()) contains parentheses. In
other programming languages, such as C, these would contain the expected arguments, but in
Bash the parentheses serve only as decoration. They are never used. Also note that the function
definition (the function itself) must appear in the script before it is called. Logically, we can’t call
something we have not defined.
In addition to passing arguments to Bash functions, we can of course return values from Bash
functions as well. Bash functions do not actually allow you to return an arbitrary value in the
traditional sense. Instead, a Bash function can return an exit status (zero for success, non-zero for
failure) or some other arbitrary value that we can later access from the $? global variable (see Table
prev article). Alternatively, we can set a global variable inside the function or use command substitution to
simulate a traditional return.
Let’s look at a simple example that returns a random number into $?:
1 | kali@kali:~$ cat funcrvalue.sh |
Notice that a random number is returned every time we run the script, because we returned the
special global variable $RANDOM (into $?). If we used the return statement without the $RANDOM
argument, the exit status of the function (0 in this case) would be returned instead.
Now that we have a basic understanding of variables and functions, we can dig deeper and discuss
variable scope.
The scope of a variable is simply the context in which it has meaning. By default, a variable has a
global scope, meaning it can be accessed throughout the entire script. In contrast, a local variable
can only be seen within the function, block of code, or subshell in which it is defined. We can
“overlay” a global variable, giving it a local context, by preceding the declaration with the local
keyword, leaving the global variable untouched. The general syntax is:
1 | local name="Joe" |
Let’s see how local and global variables work in practice with a simple example:
1 | kali@kali:~$ cat ./varscope.sh |
Let’s highlight a few key points within the code. First note that we declared two global variables,
setting name1 to John and name2 to Jason.
Then, we defined a function and inside that function, declared a local variable called name1, setting
the value to Edward. Since this was a local variable, the previous global assignment was not
affected; name1 will still be set to John outside this function.
Next, we set name2 to Lucas, and since we did not use the local keyword, we are changing the
global variable, and the assignment sticks both inside and outside of the function.
Based on this example, the following two points summarize variable scope:
• Changing the value of a local variable with the same name as a global one will not affect its
global value.
• Changing the value of a global variable inside of a function – without having declared a local
variable with the same name – will affect its global value.