1. Overview

In this tutorial, we’ll learn how we can validate the syntax of a Bash script without running it. We’ll take a closer look at some of the Bash and third-party utilities that can be used for this purpose.

2. Setup

First, let’s write a helper script, which we’re going to use in most of the tutorial:

$ cat unity_check.sh
#! /bin/bash
read -p "Enter the input: " num1
if [ -z "$num1" ]
then
    echo "The number is empty"
    exit 0
fi
if [ "${num1}" -eq 1 ]
then
   echo "Number entered is 1"
else
   echo "Not equal to One !!"
fi

This script tests whether the number entered by the user is equal to 1.

Now, let’s dive into discussion and learn the ways to validate it syntactically.

3. Syntax Checking Using noexec Mode

There can be situations where we may want to validate the script syntactically prior to its execution. If so**, we can invoke the noexec mode using the -n option**. As a result, Bash will read the commands but not execute them.

Let’s execute our unity_check.sh script in noexec mode:

$ bash -n ./unity_check.sh
$

This produces a blank output since there are no syntax errors. Now, we’ll modify our script a bit and remove the then statement from the second if condition:

$ cat unity_check_error.sh
#! /bin/bash
read -p "Enter the input: " num1
if [ -z "$num1" ]
then
    echo "The number is empty"
    exit 0
fi
if [ "${num1}" -eq 1 ]
   echo "Number entered is 1"
else
   echo "Not equal to One !!"
fi

Let’s try to execute the script:

$ ./unity_check_error.sh
Enter the input: 2
./unity_check_error.sh: line 10: syntax error near unexpected token `else'
./unity_check_error.sh: line 10: `else'

Notably, the script fails, citing “syntax error near unexpected token” as the reason. Furthermore, let’s execute it again with a blank input:

$ ./unity_check_error.sh
Enter the input:
The number is empty

Interestingly, here the script executes successfully because the second if statement is never evaluated. Now, we see that the script passed some test cases and failed for some. Hence, it makes sense to always validate the scripts syntactically before execution.

Let’s validate our script:

$ bash -n ./unity_check_error.sh
./unity_check_error.sh: line 10: syntax error near unexpected token `else'
./unity_check_error.sh: line 10: `else'

As expected, it notifies us about the syntax issues. We must also note that the user input was not asked, since the script wasn’t executed.

4. Caveats of Using noexec Mode

Let’s modify the script again — this time, we’ll remove the square bracket from the first if condition:

$ cat unity_check.sh
#! /bin/bash
read -p "Enter the input: " num1
if  -z "$num1" ]
then
    echo "The number is empty"
    exit 0
fi
if [ "${num1}" -eq 1 ]
then
   echo "Number entered is 1"
else
   echo "Not equal to One !!"
fi

Now, let’s validate the script using the bash -n option:

$ bash -n ./unity_check.sh
$ 

Interestingly, the script passes the check and no error is reported. Let’s execute the script:

$ ./unity_check.sh
Enter the input:
./unity_check.sh: line 3: -z: command not found
./unity_check.sh: line 8: [: : integer expression expected
Not equal to One !!

And, now it shows an error!

We couldn’t catch this error while syntactically validating the script using bash -n mode because the “[* ” is not a part of the syntax. **The *“[“* is an alternative for the test command, and bash -n doesn’t check the missing commands or the typos in the command.*

5. Using the Shell Check Tool

As seen in the previous section, we might miss some errors while using the bash -n option. However, we can use some third-party tools for static analysis of our shell scripts. Let’s analyze the ShellCheck tool.

First, let’s check the command to install ShellCheck utility on Debian/Ubuntu Linux distributions:

$ apt install shellcheck

And, the alternative command for RHEL/Fedora/CentOS distributions:

$ yum install ShellCheck

Now, let’s validate our script from Section 3 with this tool:

$ shellcheck unity_check_error.sh

In unity_check_error.sh line 8:
if [ "${num1}" -eq 1 ]
^-- SC1049: Did you forget the 'then' for this 'if'?
^-- SC1073: Couldn't parse this if expression. Fix to allow more checks.


In unity_check_error.sh line 10:
else
^-- SC1050: Expected 'then'.
    ^-- SC1072: Unexpected keyword/token. Fix any mentioned problems and try again.

For more information:
  https://www.shellcheck.net/wiki/SC1049 -- Did you forget the 'then' for thi...
  https://www.shellcheck.net/wiki/SC1050 -- Expected 'then'.
  https://www.shellcheck.net/wiki/SC1072 -- Unexpected keyword/token. Fix any...

As expected, it notifies us about the missing then statement. Now, let’s validate our script in Section 4 with the ShellCheck utility:

$ shellcheck unity_check.sh

In unity_check.sh line 2:
read -p "Enter the input: " num1
^--^ SC2162: read without -r will mangle backslashes.


In unity_check.sh line 3:
if  -z "$num1" ]
    ^-- SC2215: This flag is used as a command name. Bad line break or missing [ .. ]?
               ^-- SC2171: Found trailing ] outside test. Add missing [ or quote if intentional.

For more information:
  https://www.shellcheck.net/wiki/SC2171 -- Found trailing ] outside test. Ad...
  https://www.shellcheck.net/wiki/SC2215 -- This flag is used as a command na...
  https://www.shellcheck.net/wiki/SC2162 -- read without -r will mangle backs...

Great! It was able to identify the missing brackets, too, which the bash -n command was unable to identify.

6. Conclusion

In this tutorial, we checked the ways to validate the syntax of a Bash Script without running it.

First, we learned about the noexec mode of bash. Then, we checked the caveats of this mode. Finally, we analyzed a third-party tool, ShellCheck, that we can use for static analysis of our shell scripts without executing them.