Multiple flags

Fortunately for us, we do not have to be content with just a single flag: we can define many (right up until we run out of alphabet!).

We'll create a new script that will print a message to the reader. If no flags are specified, we'll print a default message. If we encounter either flag -b or flag -g, we'll print a different message, depending on the flag. We'll also include instructions for the -h flag, which will print a help message when encountered.

A script with these requirements could look something like this:

reader@ubuntu:~/scripts/chapter_15$ vim hey.sh 
reader@ubuntu:~/scripts/chapter_15$ cat hey.sh
#!/bin/bash

#####################################
# Author: Sebastiaan Tammer
# Version: v1.0.0
# Date: 2018-12-14
# Description: Getopts with multiple flags.
# Usage: ./hey.sh [flags]
#####################################

# Abstract the help as a function, so it does not clutter our script.
print_help() {
echo "Usage: $0 [flags]"
echo "Flags:"
echo "-h for help."
echo "-b for male greeting."
echo "-g for female greeting."
}

# Parse the flags.
optstring=":bgh"
while getopts ${optstring} options; do
case ${options} in
b)
gender="boy"
;;
g)
gender="girl"
;;
h)
print_help
exit 0 # Stop script, but consider it a success.
;;
?)
echo "Invalid option: -${OPTARG}."
exit 1
;;
esac
done

# If $gender is n (nonzero), print specific greeting.
# Otherwise, print a neutral greeting.
if [[ -n ${gender} ]]; then
echo "Hey ${gender}!"
else
echo "Hey there!"
fi

This script should be readable to you at this point, especially with the included comments. From the top, we start with the header and follow up with the print_help() function, which prints our help message when the -h flag is encountered (as we see a few lines further on).

Next up is the optstring, which still starts with a colon so that verbose errors from getopts is turned off (as we will handle this ourselves). In the optstring, all three flags that we will handle, that is -b, -g, and -h, are defined as a single string: bgh.

For each of these flags, we have an entry in the case statement: for b) and g), the gender variable is set to boy or girl, respectively. For h), the function we defined is called, before calling exit 0. (Think about why we would do this! If you're not sure, run the script without the exit.)

We always end a getopts block by handling unknown flags with the ?) syntax.

Moving on, after our case statements ends with esac, we get to the actual functionality. We check whether the gender variable is defined: if it is, we print a message that contains the value set according to the flag. If it is not set (that is the case if neither -b and -g are specified), we print a generic greeting that omits gender.

This is also why we exit 0 after we find -h: otherwise both the help message and the greeting would be given to the user (which is weird, since the user asks just for the help page with -h).

Let's see our script in action:

reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -h
Usage: hey.sh [flags]
Flags:
-h for help.
-b for male greeting.
-g for female greeting.
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh
Hey there!
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -b
Hey boy!
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -g
Hey girl!

So far, so good! If we call it with -h, we see the multi-line help message printed. By default, each echo ends with a newline character, so our five echoes are printed on five lines. We could have worked with a single echo and the characters, but this is more readable

If we run our script without flags, we'll see the generic greeting. Running it with either -b or -g will give the gender-specific greeting. Wasn't that easy?

It actually was! However, it is about to get a little bit more complicated. As we've explained before, users tend to be rather unpredictable, and would perhaps use too many flags, or the same flags multiple times.

Let's take a look at how our script reacts to this:

reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -h -b
Usage: hey.sh [flags]
Flags:
-h for help.
-b for male greeting.
-g for female greeting.
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -b -h
Usage: hey.sh [flags]
Flags:
-h for help.
-b for male greeting.
-g for female greeting.
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -b -h -g
Usage: hey.sh [flags]
Flags:
-h for help.
-b for male greeting.
-g for female greeting.

So, regardless of how many flags are specified, as long as the script encounters the -h flag, it will print the help message and exit (due to exit 0). For your understanding, run the preceding commands in debug with bash -x to see that they do actually differ, even though the user does not see this (hint: check for assignments of gender=boy and gender=girl).

This brings us to an important point: flags are parsed in the order they are supplied by the user! To further illustrate this point, let's look at another example of a user messing with flags:

reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -g -b
Hey boy!
reader@ubuntu:~/scripts/chapter_15$ bash hey.sh -b -g
Hey girl!

When the user supplies both the -b and -g flag, both variable assignments for gender are performed by the system. However, it seems as though the final flag is the one that wins, even though we just stated that the flags are parsed in order! Why would that be?

As always, a nice bash -x gives us a good idea of this situation:

reader@ubuntu:~/scripts/chapter_15$ bash -x hey.sh -b -g
+ optstring=:bgh
+ getopts :bgh options
+ case ${options} in
+ gender=boy
+ getopts :bgh options
+ case ${options} in
+ gender=girl
+ getopts :bgh options
+ [[ -n girl ]]
+ echo 'Hey girl!'
Hey girl!

Initially, the gender variable is assigned the value of boy. However, when the next flag is parsed, the value of the variable is overwritten with a new value, girl. Since the -g flag is the final one, the gender variable ends as girl, and thus that is what is printed.

As you will see in the next part of this chapter, it is possible to supply an argument to a flag. For flags without arguments, though, there is a really cool feature that many commands use: flag chaining. It might sound complicated, but it is actually pretty simple: if you have multiple flags, you can place them all behind a single dash.

For our script, it looks like this:

reader@ubuntu:~/scripts/chapter_15$ bash -x hey.sh -bgh
+ optstring=:bgh
+ getopts :bgh options
+ case ${options} in
+ gender=boy
+ getopts :bgh options
+ case ${options} in
+ gender=girl
+ getopts :bgh options
+ case ${options} in
+ print_help
<SNIPPED>

We specified all flags as one bunch: instead of -b -g -h, we used -bgh. As we concluded before, the flags are processed in order, which is still the case in our concatenated example (as the debug instruction clearly shows). This is not much different to an ls -al, for example. Again, this only works if a flag does not have an argument.

..................Content has been hidden....................

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