シェルスクリプトのスタイルガイドについて前回、書きました。
スタイルガイドを見ていたときに、ShellCheckというリンクがあったので、リンク先を見てみたところ、
シェルスクリプトの静的解析をするツールであることを知りました。
ということで、シェルスクリプトの静的解析ツールのShellCheckを使ってみたいと思います。
(前回、シェルスクリプトの静的解析ツールはないと書いちゃいましたが、ありましたね・・・w)
ShellCheckとは
ShellCheckの目的について、以下のことが書かれています。
(英語がスーパー苦手なので、誤訳してるかもしれません・・・)
- 初心者による構文エラーを指摘し、明確にする。
- シェルを直感で分からない、変な振る舞いで引き起こる中級レベルの問題を指摘し、明確にする。
- 微妙な警告や、コーナーケース、落とし穴を指摘する。
初級者による構文エラーから上級者でも見つけることが難しい稀なエラーに関しても検出することが可能と思われます。
ShellCheckをインストールしてみる
ShellCheckをインストールします。
インストール方法は、yumでもインストールできるようですが、今回はwgetで書庫ファイルを取得し、動作環境で解凍することにしました。
$ scversion="latest" $ wget -qO- "https://storage.googleapis.com/shellcheck/shellcheck-${scversion?}.linux.x86_64.tar.xz" | tar -xJv $ shellcheck --version ShellCheck - shell script analysis tool version: 0.7.0 license: GNU General Public License, version 3 website: https://www.shellcheck.net
ShellCheckを実行してみる
実行環境は、次の通りです。
OS | CentOS Linux release 7.5.1804 (Core) |
Bash | version 4.2.46(2)-release (x86_64-redhat-linux-gnu) |
サンプルコードはこちらを用意しました。
パラメータにKey=Value形式で渡したValueの値を返却するスクリプトです。
#!/bin/bash function getvalue() { declare -a array array+=( $@ ) for value in ${array[@]} do echo ${value} | awk -F= '{ print $2 }' done } function main() { getvalue "$@" } main "$@"
ShellCheckを実行した結果はこちらになります。
$ ./shellcheck ~/getvalue.sh In /home/vagrant/getvalue.sh line 6: array+=( $@ ) ^-- SC2206: Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a. In /home/vagrant/getvalue.sh line 8: for value in ${array[@]} ^---------^ SC2068: Double quote array expansions to avoid re-splitting elements. In /home/vagrant/getvalue.sh line 10: echo ${value} | awk -F= '{ print $2 }' ^------^ SC2086: Double quote to prevent globbing and word splitting. Did you mean: echo "${value}" | awk -F= '{ print $2 }' For more information: https://www.shellcheck.net/wiki/SC2068 -- Double quote array expansions to ... https://www.shellcheck.net/wiki/SC2206 -- Quote to prevent word splitting/g... https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
オプションなしで実行してみると、問題箇所の行と問題内容が標準出力で出力されます。
違反内容の情報は、wiki(https://www.shellcheck.net/wiki)に載っているので、出力されている違反番号(SCXXXX)で調べれば違反詳細と正しい記述に関する情報が書かれています。
では、サンプルのエラーの詳細を確認してみます。
変数はダブルクォートで囲む
変数をダブルクォートで囲むと、個々の要素のグロビングや単語分割を防ぐことができます。
サンプルスクリプトのパラメータに"aaa=bb b"と渡した場合、"bb b"の値が出力されるべきですが、ダブルクォートで囲んでいないため、単語が分割され、"bb"が出力されることになります。
静的解析結果通りに修正する
#!/bin/bash function getvalue() { declare -a array array+=( "$@" ) for value in "${array[@]}" do echo "${value}" | awk -F= '{ print $2 }' done } function main() { getvalue "$@" } main "$@"
ShellCheckを再実行すると違反はなくなり、何も出力されなくなりました。
静的解析で一部の違反番号だけを有効にしたい場合は、-i(--include)を指定することで、特定の違反番号だけを有効にすることができます。
また、一部の違反番号を除外したい場合は、-e(--exclude)を指定することで、特定の違反番号だけを除外することができます。
Jenkinsに静的解析結果を表示してみる
JenkinsのパイプラインからShellCheckを実行して、静的解析結果を出力してみます。
ShellCheckの-fオプションで、checkstyleを指定するとcheckstyle形式で静的解析結果を出力してくれます。
出力された静的解析結果は、warnings-ng-plugin を使って表示します。
ここで、注意すべき点があります。
ShellCheckは、静的解析で違反があると終了コードが0以外となるので、パイプラインでShellCheckコマンドを実行するとパイプラインが異常終了します。
そのため、ShellCheckをラップしたラッパースクリプトから実行し、ラッパースクリプトを正常終了させる必要があります。
パイプラインの定義は以下のように設定して実行しました。
pipeline { agent any stages { stage('exec') { steps { sh "/tmp/hoge.sh" } } } post { success { recordIssues(tools: [checkStyle(pattern: 'checkstyle-result.xml')]) } } }
パイプラインを実行し、静的解析結果が表示されることを確認できました。
まとめ
簡単な構文ミスから上級レベルの構文エラーまでチェックしてくれる印象を持ちました。
shの-nオプションではカバーできない範囲を大きくカバーしてくれるので、ShellCheckを実行し、問題箇所を修正した方が、確実に品質を上げることが出来ます。
Jenkinsでも静的解析結果を見ることができるので、プロジェクトにも取り入れていきたいです。