Improve your bash/sh shell script with ShellCheck lint script analysis tool

in Categories Command Line Hacks, Howto, Programming last updated November 9, 2017

ShellCheck is a static analysis tool for shell scripts. One can use it to finds bugs in your shell scripts. It is written in Haskell. You can find warnings and suggestions for bash/sh shell scripts with this tool. Let us see how to install and use ShellCheck on a Linux or Unix-like system to enhance your shell scripts, avoid errors and productivity.

Shell scripting is fun. It is useful to create nice (perhaps ugly) things in shell scripting. Shell scripts are useful for automating processes that you repeat at the prompt.
how to lint your bash sh zsh shell scripts on linux unix macos
The quality of the shell script can be dangerous. Most new users will use StackOverflow, Google, Q & A site about Linux/Unix for copy and pasting code. It can lead to lots of bad code and errors. For example, catastrophic rm command as VAR not defined at all:
rm -rf "/$VAR/*"
You can fix many such problems while writing shell scripts using a linting tool such as shellcheck.


The simplest way to install ShellCheck locally is through your package managers such as apt/apt-get/yum and friends as per your Linux distro or Unix variant.

Install ShellCheck on a Debian/Ubuntu Linux

Type the following apt command/apt-get command:
$ sudo apt install shellcheck
Sample outputs:

[sudo] password for vivek: 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,841 kB of archives.
After this operation, 15.5 MB of additional disk space will be used.
Get:1 artful/universe amd64 shellcheck amd64 0.4.6-1 [1,841 kB]
Fetched 1,841 kB in 42s (43.4 kB/s)                                                                                                                                                                               
Selecting previously unselected package shellcheck.
(Reading database ... 196100 files and directories currently installed.)
Preparing to unpack .../shellcheck_0.4.6-1_amd64.deb ...
Unpacking shellcheck (0.4.6-1) ...
Setting up shellcheck (0.4.6-1) ...
Processing triggers for man-db ( ...

Install ShellCheck on a CentOS/RHEL/Fedora/Oracle Linux

First enable EPEL repo on a CentOS/RHEL:
$ sudo yum -y install epel-release
Next, type the following yum command:
$ sudo yum install ShellCheck
Sample outputs:

Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base:
 * epel:
 * extras:
 * updates:
Resolving Dependencies
--> Running transaction check
---> Package ShellCheck.x86_64 0:0.3.5-1.el7 will be installed
--> Processing Dependency: ghc(ShellCheck-0.3.5-297097a7f5fa37100847be7f096be51e) for package: ShellCheck-0.3.5-1.el7.x86_64
Dependencies Resolved
 Package                Arch         Version                  Repository  Size
 ShellCheck             x86_64       0.3.5-1.el7              epel       495 k
Installing for dependencies:
 ghc-ShellCheck         x86_64       0.3.5-1.el7              epel       540 k
 ghc-array              x86_64         epel       113 k
 ghc-base               x86_64         epel       1.6 M
 ghc-bytestring         x86_64        epel       182 k
 ghc-containers         x86_64         epel       287 k
 ghc-deepseq            x86_64         epel        45 k
 ghc-directory          x86_64         epel        59 k
 ghc-filepath           x86_64         epel        60 k
 ghc-json               x86_64       0.7-4.el7                epel        96 k
 ghc-mtl                x86_64       2.1.2-27.el7             epel        33 k
 ghc-old-locale         x86_64         epel        50 k
 ghc-parsec             x86_64       3.1.3-31.el7             epel       105 k
 ghc-pretty             x86_64         epel        57 k
 ghc-regex-base         x86_64       0.93.2-29.el7            epel        28 k
 ghc-regex-compat       x86_64       0.95.1-35.el7            epel        15 k
 ghc-regex-posix        x86_64       0.95.2-30.el7            epel        47 k
 ghc-syb                x86_64       0.4.0-35.el7             epel        39 k
 ghc-text               x86_64           epel       379 k
 ghc-time               x86_64         epel       187 k
 ghc-transformers       x86_64           epel       100 k
 ghc-unix               x86_64         epel       160 k
Transaction Summary
Install  1 Package (+21 Dependent packages)
Total download size: 4.6 M
Installed size: 28 M
Is this ok [y/d/N]: y
Downloading packages:
(1/22): ghc-bytestring-       | 182 kB   00:09     
(2/22): ghc-array-             | 113 kB   00:09     
  ghc-parsec.x86_64 0:3.1.3-31.el7                                             
  ghc-pretty.x86_64 0:                                         
  ghc-regex-base.x86_64 0:0.93.2-29.el7                                        
  ghc-regex-compat.x86_64 0:0.95.1-35.el7                                      
  ghc-regex-posix.x86_64 0:0.95.2-30.el7                                       
  ghc-syb.x86_64 0:0.4.0-35.el7                                                
  ghc-text.x86_64 0:                                             
  ghc-time.x86_64 0:                                           
  ghc-transformers.x86_64 0:                                     
  ghc-unix.x86_64 0:                                           

If you are using a Fedora Linux, run the following dnf command:
$ sudo dnf install ShellCheck

Install ShellCheck on an Arch Linux

Type the following pacman command:
$ sudo pacman -S shellcheck

Install ShellCheck on a Gentoo Linux

Type the following emerge command:
$ sudo emerge --ask shellcheck

Install ShellCheck on an OpenSUSE Linux

Type the following zypper command:
$ sudo zypper in ShellCheck

Install ShellCheck on a macOS Unix

Type the following port command if you are using MacPorts:
$ port install shellcheck
If you use Homebrew on a macOS/OS X, type the following brew command:
$ brew install shellcheck

How to use ShellCheck

Let us see my sample shell script with the cat command:
$ cat -n backupme
Sample outputs:

     1	#!/bin/bash
     2	t="/tmp/exclude.$$"
     3	source ~/.backup.conf
     4	>$t
     5	for w in $WHATNOT
     6	do
     7		echo $w >> $t
     8	done
     9	rsync $OPT -avr --exclude-from=$t  $WHAT $SERVER:$WHERE
    10	rm -rf $t

Run shellcheck backupme in the terminal:
$ shellcheck backupme
Sample outputs:

Fig.01: ShellCheck in action (click to enlarge)
Fig.01: ShellCheck in action (click to enlarge)

The shellcheck asked me to fix unquoted variables and other issues. Here is my updated script as per warnings:
$ cat -n backupme
Sample outputs:

     1	#!/bin/bash
     2	t="/tmp/exclude.$$"
     3	source ~/.backup.conf
     4	touch $t
     5	for w in $WHATNOT
     6	do
     7		echo "$w" >> $t
     8	done
     9	rsync "$OPT" -avr --exclude-from=$t  "$WHAT" "$SERVER:$WHERE"
    10	rm -rf "$t"

How to integrate shellcheck in your text editor

You can shellcheck with VIM or emacs text editor directly. I am using neomake vim plugin. It is an asynchronous linting and make framework for Neovim/Vim. I installed it using vim-plug plugin manager in my ~/.vimrc:
call plug#begin('~/.vim/plugged')
Plug 'pearofducks/ansible-vim'
" install and use neomake linting
Plug 'neomake/neomake'
call plug#end()

To install ansible-vim and neomake/neomake type the following command in vim:
To use plugin type the following command while writing/editing your bash/sh scripts:
Sample outputs:

Fig.02: Neomake displaying warning/errors with shellcheck
Fig.02: Neomake displaying warning/errors with shellcheck


Overall it is an excellent little tool for improving and fixing your shell scripts. It can detect many common mistakes and errors. For more info see or

Posted by: Vivek Gite

The author is the creator of nixCraft and a seasoned sysadmin and a trainer for the Linux operating system/Unix shell scripting. He has worked with global clients and in various industries, including IT, education, defense and space research, and the nonprofit sector. Follow him on Twitter, Facebook, Google+.

Share this on (or read 2 comments/add one below):

2 comment

  1. I believe there’s a very small error in your catastrophic ‘rm’ example (`rm -rf “/$VAR/*”`). The first `/` makes the `rm` command fail with the message `cannot access ‘//*’: No such file or directory`.
    By removing the first `/`, the catastrophic behavior is bound to happen if $VAR do not exists (`rm -rf “$VAR/*”`).

Comments are closed.