Digital Developer Conference: Cloud Security 2021 -- Build the skills to secure your cloud and data Register free

IBM Developer Blog

Follow the latest happenings with IBM Developer and stay in the know.

Learn how to find bugs in shell scripts

This post is about ShellCheck and the power that can come from using it to find bugs in shell scripts. Over the years, I wrote a lot of code that glues things together, as a DevOps engineer, production engineer, and developer advocate. Since I spend most of my time on Linux and Mac, bash is the obvious choice for me. However, I started talking to people at different conferences and found out that the idea of linting code is still considered a developer practice, instead of an operator practice, so I’m here to try to set the record straight.


First, let me answer why I think linting code is an operator practice and you should use ShellCheck to do it. Standardizing on coding practices (yes, you have to code) will save you from insane overhead costs and tech debt in the future. When your team’s bash scripts start to look the same, they are easier to read, allowing people to start paying attention to what’s happening instead of where the do command or semicolon is. Running shellcheck enforces this. I should say that you can override ShellCheck suggestions, but that’s a team decision and out of the scope of this article. (Read up about ignoring a ShellCheck error if you need to go down this path.)


Now, let’s talk about how to use ShellCheck. There are a handful of ways to get it: apt, yum, homebrew, and even docker can be used to run this application. It’s also easy to add it to your continuous integration and continuous delivery (CI/CD) pipeline by pulling out every .sh file and running shellcheck against it.

Even adding it to your Makefile is simple:

    # Fail if any of these files have warnings
    shellcheck myscripts/*.sh

Even Travis CI has ShellCheck built-in. Just add the following to your .travis.yml:

  # Fail if any of these files have warnings
  - shellcheck myscripts/*.sh

Personally, I use a Docker container on my local laptop. So, I wrote a simple script named and any time I save a script .sh, I run against it. As you can see, it’s very straight forward:


if [ $# -eq 0 ]
    echo "You need to add an script to this check,"
    exit 1
  if [[ "$(ping -c 1 | grep '100% packet loss' )" != "" ]]
    echo "You can't seem to connect to the internet, you should probably fix that."
    exit 1
  docker pull koalaman/shellcheck:latest
  docker run -v "$PWD:/mnt" koalaman/shellcheck "$1"

Knowing my code is standardized and works as expected gives me peace of mind. The output of shellcheck shows exactly what your offending code is and there is a dedicated wiki page explaining why you should not code that way. I applaud the main developer, Vidar Holen, and his team for helping us become better developers.


Honestly, I’m just here to convince you, dear reader, to give this linter a shot. The overhead expense is minimal, and the benefits are immense. This is one of those things that you shouldn’t waste time debating, since it will make your life, and your team’s life, easier. You can integrate it into CI, run locally, and make sure everyone writes their scripts with a unified pattern for only a tad bit of work.