It used to be one of the joys of writing embedded software was never having to deploy shell scripts. But now with platforms like the Raspberry Pi becoming very common, Linux shell scripts can be a big part of a system–even the whole system, in some cases. How do you know your shell script is error-free before you deploy it? Of course, nothing can catch all errors, but you might try ShellCheck.
When you compile a C program, the compiler usually catches most of your gross errors. The shell, though, doesn’t look at everything until it runs which means you might have an error just waiting for the right path of an if statement or the wrong file name to occur. ShellCheck can help you identify those issues before deployment.
If you don’t like pasting your script into a Web page, you can install the checker locally by visiting GitHub. The readme file there also explains what kind of things the tool can catch. It can even integrate with common editors (as seen in the video below).
For example, a common issue with shell scripts is to forget to quote an environment variable that could hold a file name that contains spaces. For example, consider this really simple script:
#!/bin/bash echo Processing $1 cat $1 >$2
This works fine as long as the files have no spaces in the name. However, suppose the name of the input file is “Hack A Day.txt.” Then you get:
$ ./bad "Hack A Day.txt" hello Processing Hack A Day.txt cat: Hack: No such file or directory cat: A: No such file or directory cat: Day.txt: No such file or directory
The $1 expands to three words: Hack, A, and Day.txt. For echo, that’s not a problem. It just prints them. But the last line turns into:
cat Hack A Day.txt >hello
That’s not going to work (unless there are three files with those names at which point it will just not work correctly). The answer, of course, is to put quotes around $1 (and $2, for that matter) on the cat line. Won’t hurt on the echo line either.
ShellCheck will find those kinds of problems and more. For example, here’s part of the output from a more ambitious script:
Line 5: if [ "$0" == "$BASH_SOURCE" ] ^-- SC2128: Expanding an array without an index only gives the first element. Line 8: echo E.g.: . $0 ^-- SC2086: Double quote to prevent globbing and word splitting. Line 15: alias pathed=". $BASH_SOURCE" ^-- SC2128: Expanding an array without an index only gives the first element. ^-- SC2139: This expands when defined, not when used. Consider escaping. Line 29: if [ "$PATHEDIT_OLD" = "$PATHEDIT_NEW" -o ! -s "$PATHEDIT_FN" ] ^-- SC2166: Prefer [ p ] || [ q ] as [ p -o q ] is not well defined.
Like any similar tool, you do have to interpret the results. For example, the echo statement in line 8 doesn’t really care if the $0 has quotes around it or not, but the tool can’t figure that out.
Will ShellCheck catch everything? No. But it will catch things that you might not find for quite some time after deploying a shell script.