Source

The idea of a function library is that you define functions that are shared between different scripts. These are repeatable, generic functions that do not care too much about the specific script to work. When you create a new script, the first thing you'll do, right after the header, is include the function definitions from the library. The library is nothing more than another shell script: however, it is only used to define functions, so it never calls anything. If you were to run it, the end result would be the same as if you had run an empty script. We'll start creating our very own function library first, before we look at how we can include it.

There is only one real consideration when creating a function library: where to put it. You want to have it present just once in your filesystem, preferably in a predictable location. Personally, we prefer the /opt/ directory. However, by default /opt/ is only writable to the root user. In a multiuser system, it's probably not a bad idea to place it there, owned by root and readable by everyone, but since this is a single-user situation, we'll place it directly in our home directory. Let's make a humble beginning with our library there:

reader@ubuntu:~$ vim bash-function-library.sh 
reader@ubuntu:~$ cat bash-function-library.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-11-17
# Description: Bash function library.
# Usage: source ~/bash-function-library.sh
#####################################

# Check if the number of arguments supplied is exactly correct.
check_arguments() {
# We need at least one argument.
if [[ $# -lt 1 ]]; then
echo "Less than 1 argument received, exiting."
exit 1
fi

# Deal with arguments
expected_arguments=$1
shift 1 # Removes the first argument.

if [[ ${expected_arguments} -ne $# ]]; then
return 1 # Return exit status 1.
fi
}

Because this is a generic function, we need to first supply the number of arguments we're expecting, followed by the actual arguments. After we save the expected number of arguments, we use shift to shift all arguments one place to the left: $2 becomes $1, $3 becomes $2, and $1 is removed entirely. Doing this, only the number of arguments to check remains, with the expected number safely stored inside a variable. We then compare the two values, and if they're not the same, we return an exit code of 1. return is similar to exit, but it does not stop the script execution: if we want to do that, the script calling the function should take care of this.

To use this library function within another script, we'll need to include it. In Bash, this is called sourcing. Sourcing is achieved with the source command:

source <file-name>

The syntax is simple. As soon as you source a file, all its contents will be processed. In our library case, when we only define functions, nothing will be executed but we'll have the functions available. If you're sourcing a file that contains actual commands, such as echo, cat, or mkdir, these commands will be executed. As always, an example is worth a thousand words, so let's see how we can use source to include the library functions:

reader@ubuntu:~/scripts/chapter_13$ vim argument-checker.sh
reader@ubuntu:~/scripts/chapter_13$ cat argument-checker.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-11-17
# Description: Validates the check_arguments library function
# Usage: ./argument-checker.sh
#####################################

source ~/bash-function-library.sh

check_arguments 3 "one" "two" "three" # Correct.
check_arguments 2 "one" "two" "three" # Incorrect.
check_arguments 1 "one two three" # Correct.

Pretty simple right? We source the file using a fully qualified path (yes, even though ~ is shorthand, this is still fully qualified!) and go right on with using the function that was defined in the other script. If you run this with debug, you'll see that the function works as we expect:

reader@ubuntu:~/scripts/chapter_13$ bash -x argument-checker.sh 
+ source /home/reader/bash-function-library.sh
+ check_arguments 3 one two three
+ [[ 4 -lt 1 ]]
+ expected_arguments=3
+ shift 1
+ [[ 3 -ne 3 ]]
+ check_arguments 2 one two three
+ [[ 4 -lt 1 ]]
+ expected_arguments=2
+ shift 1
+ [[ 2 -ne 3 ]]
+ return 1
+ check_arguments 1 'one two three'
+ [[ 2 -lt 1 ]]
+ expected_arguments=1
+ shift 1
+ [[ 1 -ne 1 ]]

The first and third function call are expected to be correct, whereas the second should fail. Because we used return and not exit in our function, the script continues even after the second function call returns an exit status of 1. As the debug output shows, the second time we call the function, the evaluation 2 not equals 3 is performed and succeeds, which results in return 1. For the other calls, the arguments are correct and the default return code of 0 is returned (not shown from output, but this is really what happens; add echo $? if you want to verify for yourself).

Now, to use this in an actual script, we'll need to pass all arguments the user gives us to our function. This can be done using the $@ syntax: where $# corresponds to the number of arguments, $@ simply prints all arguments. We'll update argument-checker.sh to check arguments to the script as well:

reader@ubuntu:~/scripts/chapter_13$ vim argument-checker.sh 
reader@ubuntu:~/scripts/chapter_13$ cat argument-checker.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.1.0
# Date: 2018-11-17
# Description: Validates the check_arguments library function
# Usage: ./argument-checker.sh <argument1> <argument2>
#####################################

source ~/bash-function-library.sh

# Check user input.
# Use double quotes around $@ to prevent word splitting.
check_arguments 2 "$@"
echo $?

We pass the expected amount of arguments, 2, and all arguments received by the script, $@, to our sourced function. Run it with a few different inputs and see what happens:

reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh 
1
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh 1
1
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh 1 2
0
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh "1 2"
1
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh "1 2" 3
0

Excellent, everything seems to be working! The most interesting tries are probably the last two, since they illustrate the problem often posed by word splitting. By default, Bash will interpret every piece of whitespace as a separator. In the fourth example, we pass the "1 2" string, which is actually a single argument because of the quotes. If we did not use double quotes around $@, this would happen:

reader@ubuntu:~/scripts/chapter_13$ tail -3 argument-checker.sh 
check_arguments 2 $@
echo $?

reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh "1 2"
0

In this example, Bash passes the arguments to the function without preserving the quotes. The function would then receive "1" and "2", instead of "1 2". Something to watch out for, always!

Now, we can use a predefined function to check if the number of arguments is correct. However, currently we do not use our return code for anything. We're going to make one final adjustment to our argument-checker.sh script, which will stop script execution if the number of arguments is not correct:

reader@ubuntu:~/scripts/chapter_13$ vim argument-checker.sh 
reader@ubuntu:~/scripts/chapter_13$ cat argument-checker.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.2.0
# Date: 2018-11-17
# Description: Validates the check_arguments library function
# Usage: ./argument-checker.sh <argument1> <argument2>
#####################################

source ~/bash-function-library.sh

# Check user input.
# Use double quotes around $@ to prevent word splitting.
check_arguments 2 "$@" ||
{ echo "Incorrect usage! Usage: $0 <argument1> <argument2>"; exit 1; }

# Arguments are correct, print them.
echo "Your arguments are: $1 and $2"

Because of the page width of this book, we've broken the line with check_arguments in two by using : this signals to Bash to continue on the next line. You can omit this and have the full command on a single line, if you prefer. If we run the script now, we'll see desirable script execution:

reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh 
Incorrect usage! Usage: argument-checker.sh <argument1> <argument2>
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh dog cat
Your arguments are: dog and cat
reader@ubuntu:~/scripts/chapter_13$ bash argument-checker.sh dog cat mouse
Incorrect usage! Usage: argument-checker.sh <argument1> <argument2>

Congratulations, we have begun the creation of a function library and have successfully used it in one of our scripts!

There is a somewhat confusing shorthand syntax for source: a single dot (.). If we wanted to use that shorthand in our scripts, it would simply be . ~/bash-function-library.sh. We are, however, not big fans of this syntax: the source command is not long or complicated, while a single . can easily be missed or misused if you forget a space after it (which can be hard to see!). Our advice: know the shorthand exists if you encounter it somewhere in the wild, but use the full built-in source when writing scripts.
..................Content has been hidden....................

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