シェルスクリプトを案件先で検討する機会がある方々、いらっしゃるかと思います。
案件先でシェルスクリプトのコード規約やスタイルガイドなどがドキュメント化されていればよいのですが
そうでないパターンもあると思います。
そこで今回初めてシェルスクリプトを作成する上で参考になった
GoogleのShell Style Guideで特に参考になった部分を5つ紹介したいと思います。
1.コマンド置換
シェルスクリプト内でのコマンド置換方法としては、
バッククォート「``」とドルマークとかっこで囲む「$()」の2通りあるかと思います。
一つの方法に統一したほうが可読性が高いので一つに統一すべきですが、どちらに統一すべきかといえば
「$()」によるコマンド置換になります。
理由としては以下の例にある通り、バッククォートを使うやり方では、入れ子になった場合一つ目のコマンドと2つ目のコマンドの境目を
判断するため、バックスラッシュを入れなければなりません。
これだとどこからどこまでが一つ目のコマンド置換で2つ目のコマンド置換なのかぱっと見で把握することが難しくなります。
また入力誤りも多くなり保守性も低くなるため特に理由がない限りは$()を使用するようにしたほうが望ましいです。
# 区切りがわかりやすい
var="$(command "$(command1)")"
# 区切りがわかりづらくバックスラッシュが必要になる
var="`command \`command1\``"
2.算術演算
算術演算を行うコマンドはいくつかありますが
Google Shell Style Guideでは(( ... )) や $(( ... )) の利用を推奨しています。
(( ... ))は比較用で、$(( ... ))は算術演算用にという位置づけです。
また$[ ... ] 構文、 expr コマンド、let ビルトインは絶対に使用するなとも勧告しています。
これはなぜなのでしょうか。
letコマンドは計算結果を変数として代入するといったコマンドですが
こちらの計算結果が0となる場合、終了ステータスが失敗扱いになります。
また整数のみに対応しているため、小数点の計算では使えません。
単純な計算を行う場合であれば$(( ... ))で完全に代替可能と開発者本人も認めているとのことです。
そのためletコマンドは現在では非推奨となります。
exprコマンドを使ってはいけない一番の理由は外部コマンドであることが理由です。
シェルの組み込みコマンドではないため実行速度が遅くなり、
また文法も独特で (( ... ))のように直感的に実装することもできないため非推奨となります。
$[ ... ]コマンドは、そもそも古いbashの構文であり、特定のバージョンでは動作しないなど
互換性の問題があるため非推奨となります。
これらの理由から算術演算は$(( ... ))や(( ... ))を使うべきとのことだと思われます。
上にあげたコマンドのすべては(( ... ))で完全に代替可能な他文法もわかりやすいためです。
(
Google Shell Style Guideで十分に説明されていないところは他サイトより参考にしております)
また個人的にも他言語のような算術演算子 == != <= >=などを使用できるため(( ... ))のほうが扱いやすく直感的でわかりやすく感じました。
また、if文で数値の比較を行う際は (( ... ))を使用するべきとのことです。
そもそも[ ]や[[ ]]などの鍵括弧を使用するif文では数値としての比較というより主に文字列の比較や条件式の評価に使用されるため、数値の比較には適していません。
3.if文の[[ ]]を使用する
[[ ... ]] は [ ... ], testよりも好ましいとのことです。
理由としては、[[から]]の間では、パス名展開(ワイルドカード)や単語の分割が行われないためエラーにならないことと
正規表現の比較が可能だったりそのほかの拡張機能がtestや[ ... ]に比べて豊富なため、[[ ... ]]を使用するほうが好ましいとのことです。
以下に例を示します。
# 正規表現でマッチする
if [[ "filename" == f* ]]; then
echo "Match"
fi
# これは "f*" そのものにマッチする (このケースではマッチしない)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# これは f* がカレントディレクトリのコンテンツに展開されるため、
# "too many arguments" エラーとなる。
if [ "filename" == f* ]; then
echo "Match"
fi
また[[ ... ]]の拡張機能の他には、他言語にあるような文字列の長さ比較や
辞書順の比較、[[ command1 && comand2 ]]や[[ command1 || command2 ]]などの論理演算子が利用できるとのことです。
そのため特に理由がない場合は、算術演算子のif文以外 は全て[[ ... ]]で統一することが望ましいといえます。
4.Shell Checkの導入
こちらはシェルの書き方というよりシェルを作成するときに必要なものになりますが
Shell Checkはバグや文法上のエラー、警告、コマンドの推奨非推奨などを教えてくれるツールになります。
自分はVScodeにプラグインを入れて導入しておりますが、やはりシェルは独特な文法も多いため
見逃しが発生しやすいので可能なら間違いなく導入したほうが良いかと思っております。
終了ステータスが取得できないことや、意図しているコマンドの使い方ができていないなど細かく教えてくれます。
5.functionや実行コードの位置
シェルをすでにゴリゴリ書いている方々にとっては当たり前すぎることかもしれませんが
初めてシェルスクリプトを書く上でほかの言語とやっぱりすごく変わってるなと感じるのが
実行コードをどこにでも記載できるということでした。
Google Shell Style Guideに書いてあることですが、おそらくこれに関してはどの現場でも順守してることのように思えます。
functionは定数やinclude文の下に書き、main関数の上に書くこと。
また実行コードは関数と関数の間に書いたりしてデバックししづらくするのではなく、main関数にまとめて
最後の行にmain関数の実行コードを記載することです。
以上でGoogle Style Guideで特に参考になった部分の紹介になります。
誤りや抜け漏れ等ありましたらご指摘いただけますと幸いです。
【参考】
https://google.github.io/styleguide/shellguide.html#s6.2-command-substitution
https://qiita.com/ko1nksm/items/8fc8216cb94ec272c400
https://qiita.com/takech111/items/19ca012d8d09216e7d56