Shell のコーディングに便利な shellcheck/shfmt の紹介
みなさんこんにちは、くるふとです。note 初投稿です。
ナビタイムジャパンでは、経路探索エンジンや時刻表データのリリースフロー改善を主に担当しています。
今回は Shell のコーディングに便利な shellcheck と shfmt をご紹介します。
Shellcheck について
shellcheck は、バグの要因になり得る記述を検知し警告してくれる、Shell の Linter です。
なぜ Linter が必要なのか
まずは、下記のシェルスクリプトをご覧ください。
#!/bin/bash
set -euo pipefail
# 受け取った引数を順番に出力する。
for v in $@; do
echo "${v}"
done
受け取った引数を for 文で回し出力するという、よくあるようなスクリプトに見えます。しかし、このスクリプトは以下のような場合に意図したものとは違う挙動を取ります。
$ ./sample.sh "AAA BBB CCC" "DDD EEE FFF"
AAA
BBB
CCC
DDD
EEE
FFF
# 本当は下記のように出力して欲しい
# AAA BBB CCC
# DDD EEE FFF
後者のように出力したいのであれば、このスクリプトは $@ を "$@" と表記する必要がありました。
Shell を書いていると、このような 潜在的なエラー・バグ がどうしても生まれてしまいます。人間のチェックのみでこれらを全て潰すのはかなり難しいです。
こういったエラー・バグを潰すため、 Shell のコーディングでも Linter が重要になってきます。
Install
# Linux
$ yum -y install epel-release
$ yum install ShellCheck
# Mac
$ brew install shellcheck
使い方
下記のようにコマンドを打つと、スクリプト内のコードをチェックして、警告を出してくれます。
$ cat ./sample.sh
#!/bin/bash
set -euo pipefail
function main() {
local message="Hello World!"
echo ${message}
}
main "$@"
$ shellcheck ./sample.sh
In ./shellcheck_sample.sh line 6:
echo ${message}
^--------^ SC2086: Double quote to prevent globbing and word splitting.
Did you mean:
echo "${message}"
For more information:
https://www.shellcheck.net/wiki/SC2086 -- Double quote to prevent globbing ...
プラグインについて
vscode 用のプラグインもあるので紹介いたします。
下記のように、エディタ上にポップアップで警告を出してくれるので、非常に使いやすいです。
Shfmt について
shfmt は、スクリプト内の記述を設定したルールにしたがって整形してくれる Formatter となります。
なぜ Formatter が必要なのか
下記の例をご覧ください。
# A
for v in "$@";
do
if [ "${flag}" = "ok" ];
then
echo "${v}"
fi
done
# B
for v in "$@"; do
if [[ "${flag}" == "ok" ]]; then
echo "${v}"
fi
done
A と B は同じ挙動をします。しかし、制御文の書き方やインデント幅があっていないですね。
このように同じスクリプトもしくはリポジトリ内に、書き方の差分があると処理も追いにくいですし、後から変更を加える際もどちらに合わせるべきか悩ましいです。こういった 開発時の本質的ではない負担 は、なるべく軽減したいです。このような状況で、 Formatter は活躍します。
Install
# Linux
$ curl -L https://github.com/mvdan/sh/releases/download/v3.1.1/shfmt_v3.1.1_linux_amd64 -o ./shfmt
$ chmod 755 ./shfmt
$ mv ./shfmt /usr/local/bin/
# Mac
$ brew install shfmt
使い方
下記のようにコマンドを打つと、スクリプト内のコードを整形してくれます。
$ cat ./sample1.sh
#!/bin/bash
set -euo pipefail
flag=ok
for v in "$@";
do
if [[ "${flag}" == "ok" ]];
then
echo "${v}"
fi
done
$ shfmt -w ./sample1.sh # 整形して保存
$ cat ./sample1.sh
#!/bin/bash
set -euo pipefail
flag=ok
for v in "$@"; do
if [[ "${flag}" == "ok" ]]; then
echo "${v}"
fi
done
また、shfmt は EditorConfig をサポートしています。EditorConfig はチーム間で一貫したコーディングスタイルを保持するための仕組みです。
下記のようなファイルを .editorconfig というファイル名でリポジトリに追加しておけば、リポジトリ内のシェルスクリプトがその設定でフォーマットされるようになります。
# editorconfig sample
[*.sh]
indent_style = space # インデントをスペースで統一
indent_size = 2 # インデントサイズを2で統一
プラグインについて
shfmt にもvscode用のプラグインが存在します。
vscode 上で Preferences -> Setting -> Format On Save にチェックを入れてあげると、保存時に自動で整形してくれるので、大変便利です。
保存前
保存後
まとめ
Shell のコーディングは、バグが混入しやすかったり、人によって書き方が違ったりと、何かと苦労が絶えないです。
shellcheck と shfmt を導入すると、こういった問題が解消されやすいかなと思います。ぜひ、インストールしてみてください。