Technical Exasol

Ghost-in-the-shell busters: your guide to using ShellCheck for top quality scripts

28 May 2019 | Share

Virtual Schema

As software engineers, we always strive to follow best practices when developing our projects. By that I mean, with each code commit or pull request we like to run automated checks to ensure the quality or security of our code.

But occasionally, some Bash or shell script with code which has been copy-pasted from the internet finds its way into the project – and these scripts inevitably behave unexpectedly when tested across a range of different scenarios.

That’s where ShellCheck comes into its own – and it could potentially save you a heck of a lot of time in your day-to-day software development.

 What is ShellCheck ?

ShellCheck is a Bash or sh script linting tool that can be used to detect the quality violations – and ensure that scripts follow the best practices.

Here are some of the main uses of ShellCheck:

  • it detects various types of inaccurate quoting
  • it can identify many incorrect conditional statements
  • it recognizes the misuse of commands
  • it detects syntax errors
  • it make suggestions for improving the robustness of the script
  • it warn you about portability issues
  • and many more

How to use it – common lines and what they mean

I have run the ShellCheck in one of our repositories and found the following lines. Here are my suggestions for what to do with them:

  • echo "FAILED: $cmd"
    • SC2154: cmd is referenced but not assigned.
    • This is an indication that you should double check the variable name  – and make sure it’s being spelt correctly.
  • for test in $folder/*.py; do
    • SC2231: Quote expansions in this for loop glob to prevent wordsplitting, e.g. “$dir”/*.txt .
    • When iterating over globs containing expansions, make sure glob characters are outside quotes. For example, “$dir/*.txt” will not glob expand, but “$dir”/*.txt or “$dir”/*.”$ext” will.
  • while read line
    • SC2162: read without -r will mangle backslashes.
    • By default, read will interpret backslashes before spaces and line feeds, and otherwise strip them. This is rarely expected or desired. You should make sure that is what you intended.
  • if echo $line | grep -F = &>/dev/null
    • SC2086: Double quote to prevent globbing and word splitting.
    • Quoting variables prevents the script from breaking when input contains spaces, line feeds, or glob characters.
  • if [ ! -z "$single_test" ]; then
    • SC2236: Use -n instead of ! -z.
    • You have negated test -z or test -n, resulting in a needless double-negative. This is a syntax issue that doesn’t affect the overall correctness of the script.

How to suppress ShellCheck warnings

If you think that the warning or issue suggested by the ShellCheck isn’t important or valid (for instance, anything related to styling), you can suppress them.

To do this, you can add a comment inside the shell file:

hexToAscii() {
  # shellcheck disable=SC2059
  printf "\x$1"
}

Or extend the SHELLCHECK_OPTS variable with ignored checks:

export SHELLCHECK_OPTS="-e SC2059 -e SC2034 -e SC1090"

How to install and run ShellCheck in Travis CI

By default, ShellCheck isn’t installed in Travis CI virtual machines or containers. But it is very easy to do so. Simply follow these steps:

First, add this line into your .travis.yml file:

dist: trusty

addons:
  apt:
    packages:
      - shellcheck

Then, in order to run ShellCheck with each build trigger, you can add this line into the .travis.yml file:

script:
  - find . -name "*.sh" -print0 | xargs -n 1 -0 shellcheck

And that’s it. Don’t you just love simple fixes?

Conclusion

In this blog, we introduced the ShellCheck linting tool for Bash and shell scripts. We hope this guide helps you write shell scripts which conform to best practices and have consistent syntax – so they’re always easy to maintain, robust and portable.  That way, if anyone mentions Ghost in the Shell when you’re coding you can happily refer them to Manga.

Written by Muhammet Orazov

Considering implementing an in-memory database?

Free White Paper

Looking For An In-Memory Analytic Database