Shellcheck - A Static Analysis Tool for Shell Scripts - Introduction
ShellCheck is a static analysis tool, or linter, for shell scripts. It detects various types of errors, provides suggestions, and issues warnings for shell scripts. ShellCheck identifies syntax issues, semantic problems that may cause shell scripts to behave unexpectedly, and other corner cases.
Different examples of poorly written shell code are presented in the following sections. The utilization of ShellCheck to identify errors, offer suggestions, and issue warnings in these examples is detailed.
The currently supported shells include: bash, dash and ksh.
Installation
To install ShellCheck on Ubuntu/Debian-based Linux systems, use the following command:
1
sudo apt install shellcheck
Usage of Shellcheck with bad code examples
Create a demo directory to practice the ShellCheck tool with the following commands:
1
2
mkdir -p ~/shellcheck_demo
cd ~/shellcheck_demo
Checking quotes
Here is a shell script named quotes.sh
that contains various poorly written examples:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
# Example 1
print_seed() {
cat $1
}
echo "animal mammal dinner hot now yesterday" > "Seed Phrase"
print_seed "Seed Phrase"
# Example 2
touch file1.txt file2.txt
find ./ -name *.txt
# Example 3
touch "~/myfile.txt"
# Example 4
echo 'The PATH is $PATH'
Attempt to execute this script and observe if the output aligns with your expectations.
1
2
chmod +x ./quotes.sh
./quotes.sh
Checking the script with shellcheck
Now, let’s identify the issues in the above script using the shellcheck command.
1
shellcheck quotes.sh
Here is the report:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
In quotes.sh line 5:
cat $1
^-- SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean:
cat "$1"
In quotes.sh line 14:
find ./ -name *.txt
^---^ SC2061 (warning): Quote the parameter to -name so the shell won't interpret it.
^-- SC2035 (info): Use ./*glob* or -- *glob* so names with dashes won't become options.
In quotes.sh line 17:
touch "~/myfile.txt"
^----------^ SC2088 (warning): Tilde does not expand in quotes. Use $HOME.
In quotes.sh line 20:
echo 'The PATH is $PATH'
^-----------------^ SC2016 (info): Expressions don't expand in single quotes, use double quotes for that.
For more information:
https://www.shellcheck.net/wiki/SC2061 -- Quote the parameter to -name so t...
https://www.shellcheck.net/wiki/SC2088 -- Tilde does not expand in quotes. ...
https://www.shellcheck.net/wiki/SC2016 -- Expressions don't expand in singl...
Analyzing above report
The output provides a clear explanation of the identified issues. Nevertheless, let’s delve into the details for a better understanding:
In the example 1 - line 5: $1 should be in quotes.
In the example 2 - line 14: Wrap *.txt in the quotes.
In the example 3 - line 17: Tilde won’t exapand inside the double quotes. We can use $HOME
In the example 4 - line 20: $PATH won’t expand in single quotes. Use double quoes.
Here is the updated quotes.sh script after addressing the errors/warnings pointed out by the shell script tool:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash
# Example 1
print_seed() {
cat "$1"
}
echo "animal mammal dinner hot now yesterday" > "Seed Phrase"
print_seed "Seed Phrase"
# Example 2
touch file1.txt file2.txt
find ./ -name "*.txt"
# Example 3
touch "$HOME/myfile.txt"
# Example 4
echo "The PATH is $PATH"
Checking conditionals
Here is the conditionals.sh
script with examples illustrating various conditional-related issues.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
# Example 1
n=2
if [[ n != 2 ]]; then
echo "n is not 2";
else
echo "n is 2";
fi
## `[[ n != 2 ]]` returns true always. And echo statement will not print.
# Example 2
touch file1.txt
if [[ -e *.txt ]]; then
echo "file1.txt present"
else
echo "file1.txt not present"
fi
# Example 3
n=3
if [[ ${n}==2 ]]; then
echo "n is 2";
else
echo "n is not 2";
fi
# Example 4
n=""
if [[ -n "$n " ]];then
echo "n is not empty"
else
echo "n is empty"
fi
Running shellcheck on the script
Run the script and verify if the output aligns with expectations
1
shellcheck conditionals.sh
Here is the analysis:
In example 1 - line 5: It should be $n
instead of n.
In example 2 - line 16: -e can’t be used with *.txt. Either use file path or use for loop to check all the *.txt files.
In example 3 - line 24: There should be spaces around the comparison operator.
In example 4 - line 33: The if condition is always true regardless of n value because there is a space inside the quotes. Remove it.
Shellcheck can identify various types of conditional issues, including the following.
- Comparing numerical values with strings
- Using logical operators inside brackets.
- Accidental backgrounds and piping [[ condition1 ]] & [[ condition 2 ]] | [[ condition 3 ]]
Checking syntax errors
Here are a few examples of syntax issues in shell scripts. Refer to the comments for the corresponding fixes.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
n = 42 # Spaces shouldn't be there around = operator while assignments
$n=42 # $ should not be used when assigning a value to a variable
# Shouldn't use $ with loop variable (i). Following is wrong
for $i in {1..10};do
# Do something here
done
n=1
num$n="36" # Use arrays instead
arr=(1, 2, 3) # Commas can't be used when defining arrays
echo $arr[14] # Missing {} in array references
else if condition; then .. # Using 'else if' instead of 'elif'
func; func() { echo "hello world; } # Using function before definition
Checking protability between shells
Here are a few examples that may run without issues in one shell variant but pose problems in others. Shellcheck does a good job of detecting these issues.
1
2
echo {1..10} # Works in bash and ksh. Doesn't work in sh or dash
local var=2 # local keyword is undefined in sh
Miscellaneous checking
Following are few more bad examples:
1
2
3
4
5
6
7
echo 'Don't do it' # Three apostrophes. Unclosed single quote.
cat file1.txt | grep word # cat is unnecessary. Use - grep word file1.txt
echo "Today date is `date`" # Use $ instead of backticks for date command
echo $(( $a + 2 )) # Don't use $ on variables inside the $((..)).
# Might not work in all shells.
rm -rf "$PROJECTROOT/"* # Warns user the possibility of removing root directory if PROJECTROOT is empty
Getting additional help on shellcheck
The shellcheck help command
The shellcheck --help
command gives the brief info and options supported for the shellcheck tool.
1
shellcheck --help
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Usage: shellcheck [OPTIONS...] FILES...
-a --check-sourced Include warnings from sourced files
-C[WHEN] --color[=WHEN] Use color (auto, always, never)
-i CODE1,CODE2.. --include=CODE1,CODE2.. Consider only given types of warnings
-e CODE1,CODE2.. --exclude=CODE1,CODE2.. Exclude types of warnings
-f FORMAT --format=FORMAT Output format (checkstyle, diff, gcc, json, json1, quiet, tty)
--list-optional List checks disabled by default
--norc Don't look for .shellcheckrc files
-o check1,check2.. --enable=check1,check2.. List of optional checks to enable (or 'all')
-P SOURCEPATHS --source-path=SOURCEPATHS Specify path when looking for sourced files ("SCRIPTDIR" for script's dir)
-s SHELLNAME --shell=SHELLNAME Specify dialect (sh, bash, dash, ksh)
-S SEVERITY --severity=SEVERITY Minimum severity of errors to consider (error, warning, info, style)
-V --version Print version information
-W NUM --wiki-link-count=NUM The number of wiki links to show, when applicable
-x --external-sources Allow 'source' outside of FILES
--help Show this usage summary and exit
We can make shellcheck ignore certain error codes using the option –exclude=CODE1,CODE2… Useful while deploying the shellcheck in some build system.
Report from the shellcheck output
If you review the Shellcheck output for the quotes.sh script, each error, warning, or suggestion is accompanied by a URL at the end of the report for additional reference. It looks something like the following:
1
2
3
4
For more information:
https://www.shellcheck.net/wiki/SC2061 -- Quote the parameter to -name so t...
https://www.shellcheck.net/wiki/SC2088 -- Tilde does not expand in quotes. ...
https://www.shellcheck.net/wiki/SC2016 -- Expressions don't expand in singl...
Feel free to open those URLs in the browser and read the detailed info about the issue there.
Page consisting of all error codes
You can find the all shellcheck error codes here. If this link is broken, you can find the modified web page link here