Part 3. Debugging a shell script

In this final part, you’re going to use the ShellCheck extension you just installed to debug a short Bash program.

Your task

Create a new file in Visual Studio Code by selecting New File… from the File menu. Name the file hello.sh and save it in your home directory.

Copy the short program below into hello.sh and save the file.

#!/bin/bash

if [[ $# -gt 0 ]]; then
    user="$1"
else
    user="$(whoami)"
fi

echo Hello ${user}, welcome to CS 241

Info

The line that starts with #! is called a shebang and it tells the operating system what program to use to run the file, namely /bin/bash.

Next is a somewhat unusual looking if statement that sets the variable user. If the user runs the script and passes foo as an argument like this

$ bash hello.sh foo

then user will be set to foo. If the user runs the script with no arguments like this

$ bash hello.sh

then user will be set to name of the current user by calling the whoami program.

The final line of the program prints text. Notice that ${user} is used to get the value of the user variable. (You can also use $user without the braces, but there are situations where the braces are required so I tend to use braces all the time.)

Open a terminal in VS Code by pressing Ctrl-` (control-backtick). Run the program a few times with different arguments and pay close attention to how the program behaves.

$ bash hello.sh
$ bash hello.sh Stu Dent
$ bash hello.sh "Stu Dent"
$ bash hello.sh "Stu         Dent"

Tip

Notice that $ bash hello.sh Stu Dent didn’t include the Dent portion whereas when the quotation marks were used, the full name appeared. Bash uses spaces to split the command into different parts. This is called word splitting. Stu and Dent became separate arguments passed to hello.sh. By using quotation marks, the whole string stays together as a single argument.

This will come up often when working with the terminal.

You may have noticed strange behavior with the final two examples. Namely, "Stu Dent" and "Stu Dent" behaved identically:

$ bash hello.sh "Stu         Dent"
Hello Stu Dent, welcome to CS 241

Where did the extra spaces go?

Look in VS Code and you’ll notice that ${user} has a squiggly line under it. Mouse over the line and you’ll see that the ShellCheck extension has reported an error here. You can see a list of all problems by opening the Problems pane by selecting Problems from the View menu.

ShellCheck says, “Double quote to prevent globbing and word splitting. shellcheck(SC2086).” Actually, the ShellCheck extension merely runs the shellcheck program. If we run shellcheck ourselves using the terminal as shown below, it will give us some more detail:

$ shellcheck hello.sh

In hello.sh line 9:
echo Hello ${user}, welcome to CS 241
           ^-----^ SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean: 
echo Hello "${user}", welcome to CS 241

For more information:
  https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...

In addition to showing the error, it also suggests and fix and provides a link for more information.

The underlying problem here is that ${user} expanded to

Stu         Dent

but this underwent word splitting when evaluating the echo line. echo takes any number of arguments and prints them out in a line with a single space between them. So when executing the line

echo Hello ${user}, welcome to CS 241

it first expanded ${user} giving

echo Hello Stu         Dent, welcome to CS 241

This is split by spaces and thus executes exactly as if it had been written like this.

echo Hello Stu Dent, welcome to CS 241

Modify hello.sh by following the suggestion given by shellcheck and save your file. Run the program again. This time, any spaces should be preserved in the argument.

Tip

In general, when programming in Bash, you want to double-quote variables otherwise the variables undergo word splitting and you almost never want that.

See the ShellCheck Wiki for error SC2086 for more explanation as well as a suggestion for a better way to use quotation marks in the echo line in hello.sh.