「ShellCheck」を使って、メンテナンス性の高いシェルスクリプトを実装する

ShellCheckとは

ShellCheckは、シェルスクリプトの静的解析ツールで、マズい書き方をしてると怒ってくれるLinterだ。

たとえば、example.sh という下記のシェルスクリプトがあるとしよう。

echo $0

これをShellCheckでチェックすると、こんな警告を出してくれる。

In example.sh line 1:
echo $0
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.
     ^-- SC2086: Double quote to prevent globbing and word splitting.

この例では、「shebangを書け」「変数使うときはダブルクォート使え」的な指摘をしてくれる。

インストール

公式でDockerイメージが提供されているので、それを使うのが簡単だ。筆者はDocker版を使っている。

$ docker pull koalaman/shellcheck:stable

macOSであれば、Homebrewでインストールしてもよい。

$ brew install shellcheck

使い方

ここではDocker版をベースに紹介する。

ベーシックな使い方

単純にチェックしたいファイルを指定して実行する。

$ docker run --rm -t -v "$PWD:/mnt" koalaman/shellcheck example.sh

余談だが、docker runの-tオプションがないと、カラー出力されないので、Docker版使う場合は認識しておこう。

特定のルールを無視する

記事の冒頭で紹介した例で、SC2086だけ除外したいという場合は、--exclude オプションを指定しよう。

$ docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --exclude=SC2086 example.sh

In example.sh line 1:
echo $0
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.

出力フォーマットの指定

--formatオプションで指定できる。選べるフォーマットは「tty、jsoncheckstylegcc」だ。

tty

デフォルトはttyで、何も指定しない場合と同様の結果になる。

$ docker run --rm -t -v "$PWD:/mnt" koalaman/shellcheck --format=tty example.sh

In example.sh line 1:
echo $0
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.
     ^-- SC2086: Double quote to prevent globbing and word splitting.

json

他のCLIツールと組み合わせるなら、json形式が役立つかもしれない。

$ docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --format=json example.sh | jq .
[
  {
    "file": "example.sh",
    "line": 1,
    "endLine": 2,
    "column": 1,
    "endColumn": 1,
    "level": "error",
    "code": 2148,
    "message": "Tips depend on target shell and yours is unknown. Add a shebang."
  },
  {
    "file": "example.sh",
    "line": 1,
    "endLine": 1,
    "column": 6,
    "endColumn": 8,
    "level": "info",
    "code": 2086,
    "message": "Double quote to prevent globbing and word splitting."
  }
]

checkstyle

CIでCheckStyleを使っている場合は、checkstyle形式で出力すると便利だろう。

$ docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --format=checkstyle example.sh
<?xml version='1.0' encoding='UTF-8'?>
<checkstyle version='4.3'>
<file name='example.sh' >
<error line='1' column='1' severity='error' message='Tips depend on target shell and yours is unknown. Add a shebang.' source='ShellCheck.SC2148' />
<error line='1' column='6' severity='info' message='Double quote to prevent globbing and word splitting.' source='ShellCheck.SC2086' />
</file>
</checkstyle>

gcc

gcc系のエラーメッセージのフォーマットっぽい。(よく知らない

docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --format=gcc example.sh
example.sh:1:1: error: Tips depend on target shell and yours is unknown. Add a shebang. [SC2148]
example.sh:1:6: note: Double quote to prevent globbing and word splitting. [SC2086]

severityの指定

--severityオプションでseverityを指定できる。「error、warning、info、style」から選択でき、何も指定しない場合、styleが指定されたものとして動くらしい。

たとえば、最初の例をerrorレベルにした場合、警告の数が減る。

$ docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --severity=error example.sh

In example.sh line 1:
echo $0
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.

infoレベルにすると、警告は元通り表示される。

$ docker run --rm -v "$PWD:/mnt" koalaman/shellcheck --severity=info example.sh

In example.sh line 1:
echo $0
^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang.
     ^-- SC2086: Double quote to prevent globbing and word splitting.

ヘルプ

なぜか--helpオプションが実装されてないが、引数なしで実行するとちゃんと表示してくれる。

$ docker run --rm koalaman/shellcheck
No files specified.

Usage: shellcheck [OPTIONS...] FILES...
  -a                --check-sourced          Include warnings from sourced files
  -C[WHEN]          --color[=WHEN]           Use color (auto, always, never)
  -e CODE1,CODE2..  --exclude=CODE1,CODE2..  Exclude types of warnings
  -f FORMAT         --format=FORMAT          Output format (checkstyle, gcc, json, tty)
  -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
  -x                --external-sources       Allow 'source' outside of FILES

バージョン

オーソドックスに--versionオプションを使えばOK。

$ docker run --rm koalaman/shellcheck --version
ShellCheck - shell script analysis tool
version: 0.5.0
license: GNU General Public License, version 3
website: https://www.shellcheck.net

シェルスクリプト内に除外設定を定義

あんまり乱用すべきではないが、このワーニングだけは許してくれ!ってときには、ソースコードに直接除外設定を書くこともできる。

# shellcheck disable=SC2148
echo $0

ルールの詳細

怒られたけど何をすればいいのか分からないという場合は、各ルールの詳細を参照しよう。

GitHub

詳細はGitHubのWikiからたどることができる。少しわかりづらいが、右側のPagesをクリックすると、Wikiの記事一覧が出てくるので、そこから目当ての記事を探せばよい。

例えば、SC2086について知りたい場合は、下記URLにアクセスする。

ターミナル

ブラウザを開くのがメンドウであれば、~/.zshrcとか~/.bashrcとかに雑にfunction定義しておくと、ターミナルで確認できて便利である。

function schelp() {
  curl -s https://raw.githubusercontent.com/wiki/koalaman/shellcheck/"$1".md
}

おわりに

シェルスクリプトは適当に書き散らすことも多いが、ShellCheckを使えばメンテナンス性の高いコードを実装する一助になるので、ぜひ一度試しみてはいかがだろうか。