Variable scopes

While functions are great, there are some things we have previously learned that we'll need to reconsider in the scope of functions, most notably variables. We know that variables store information that can be accessed or mutated multiple times and at multiple points in our scripts. However, something we have not yet learned is that a variable always has a scope. By default, variables are scoped globally, which means they can be used throughout the script at any point. With the introduction of functions also comes a new scope: local. Local variables are defined within a function and live and die with the function call. Let's see this in action:

reader@ubuntu:~/scripts/chapter_13$ vim functions-and-variables.sh
reader@ubuntu:~/scripts/chapter_13$ cat functions-and-variables.sh
#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-11-11
# Description: Show different variable scopes.
# Usage: ./functions-and-variables.sh <input>
#####################################

# Check if the user supplied at least one argument.
if [[ $# -eq 0 ]]; then
echo "Missing an argument!"
echo "Usage: $0 <input>"
exit 1
fi

# Assign the input to a variable.
input_variable=$1
# Create a CONSTANT, which never changes.
CONSTANT_VARIABLE="constant"

# Define the function.
hello_variable() {
echo "This is the input variable: ${input_variable}"
echo "This is the constant: ${CONSTANT_VARIABLE}"
}

# Call the function.
hello_variable
reader@ubuntu:~/scripts/chapter_13$ bash functions-and-variables.sh teststring
This is the input variable: teststring
This is the constant: constant

So far, so good. We can use our global constants in a function. This is not surprising, since it is not called a global variable lightly; it can be used anywhere in the script. Now, let's see what happens when we add some extra variables in the function:

#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.1.0
# Date: 2018-11-11
# Description: Show different variable scopes.
# Usage: ./functions-and-variables.sh <input>
#####################################
<SNIPPED>
# Define the function.
hello_variable() {
FUNCTION_VARIABLE="function variable text!"
echo "This is the input variable: ${input_variable}"
echo "This is the constant: ${CONSTANT_VARIABLE}"
echo "This is the function variable: ${FUNCTION_VARIABLE}"
}

# Call the function.
hello_variable

# Try to call the function variable outside the function.
echo "Function variable outside function: ${FUNCTION_VARIABLE}"

What do you think happens now? Give it a try:

reader@ubuntu:~/scripts/chapter_13$ bash functions-and-variables.sh input
This is the input variable: input
This is the constant: constant
This is the function variable: function variable text!
Function variable outside function: function variable text!

Contrary to what you might have suspected, the variable we defined inside the function is actually still a global variable (sorry for tricking you!). If we wanted to use locally scoped variables, we need to add the built-in local shell:

#!/bin/bash
#####################################
# Author: Sebastiaan Tammer
# Version: v1.2.0
# Date: 2018-11-11
# Description: Show different variable scopes.
# Usage: ./functions-and-variables.sh <input>
#####################################
<SNIPPED>
# Define the function.
hello_variable() {
local FUNCTION_VARIABLE="function variable text!"
echo "This is the input variable: ${input_variable}"
echo "This is the constant: ${CONSTANT_VARIABLE}"
echo "This is the function variable: ${FUNCTION_VARIABLE}"
}
<SNIPPED>

Now, if we execute it this time, we'll actually see the script misbehaving at the final command:

reader@ubuntu:~/scripts/chapter_13$ bash functions-and-variables.sh more-input
This is the input variable: more-input
This is the constant: constant
This is the function variable: function variable text!
Function variable outside function:

Because of the local addition, we can now only use the variable and its content inside of the function. So, when we call the hello_variable function, we see the content of the variable, but when we try to print it outside of the function in echo "Function variable outside function: ${FUNCTION_VARIABLE}", we see it is empty. This is the expected and desirable behavior. What you can actually do, and is sometimes really convenient, is the following:

#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.3.0
# Date: 2018-11-11
# Description: Show different variable scopes.
# Usage: ./functions-and-variables.sh <input>
#####################################
<SNIPPED>
# Define the function.
hello_variable() {
local CONSTANT_VARIABLE="maybe not so constant?"
echo "This is the input variable: ${input_variable}"
echo "This is the constant: ${CONSTANT_VARIABLE}"
}

# Call the function.
hello_variable

# Try to call the function variable outside the function.
echo "Function variable outside function: ${CONSTANT_VARIABLE}"

Now, we've defined a locally scoped variable with the same name as a globally scoped one we have already initialized! You might have an idea about what happens next, but be sure to run the script and understand why this happens:

reader@ubuntu:~/scripts/chapter_13$ bash functions-and-variables.sh last-input
This is the input variable: last-input
This is the constant: maybe not so constant?
Function variable outside function: constant

So, when we used the CONSTANT_VARIABLE variable (remember, constants are still considered variables, albeit special ones) within the function, it printed the value of the locally scoped one: maybe not so constant?. When outside the function, in the main body of the script, we printed the value for the variable again, and we were presented with the value as we had originally defined it: constant.

You might be having a hard time imagining a use case for this. While we agree that you will probably not use this often, it does have its place. For example, imagine a complex script where a global variable is used by multiple functions and commands sequentially. Now, you might come across a situation where you need the value of the variable, but slightly modified to use it correctly in a function. You also know that functions/commands further on need the original value. Now, you could copy the contents to a new variable and use that, but by overriding the variable within a function you make it much clearer to the reader/user that you have a purpose for this; that it is a well-informed decision and you're aware you need that exception for just that function. Using a locally scoped variable (preferably with a comment, as always) will ensure readability!

Variables can be set read-only by using the declare built-in shell. If you check the help, with help declare, you'll see it described as 'Set variable values and attributes'. A read-only variable such as a constant can be created by replacing CONSTANT=VALUE with declare -r CONSTANT=VALUE. If you do this, you can no longer (temporarily) override a variable with a local instance; Bash will give you an error. In practice, the declare command is not used too much as far as we have encountered, but it can serve useful purposes besides read-only declarations, so be sure to give it a look!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.96.94