setとオプションを使って安全にシェルスクリプトを書こう

こんにちは、さるまりんです。

シェルスクリプトを書いていて、動くんだけどなんか不安定って時ありませんか?
プログラムなんだから、本当は不安定なのはよくないですよね。
でも、ちょっとした“変”って、意外とつきものです。

そんな時はこれ↓をスクリプトのトップに書いておきましょう。

#!/bin/bash
set -euo pipefail

これをやっていると思ってもなかったエラーから守ってくれるかもしれません。

ひとつずつ見ていきます。

set -e : エラーがあったら即終了!

set -e

これはコマンドが1つでも失敗したら、そこでスクリプトの実行を即終了させるオプションです。
「失敗」とはコマンドが0以外の終了コードを返した時です。

例:set -eなし
echo "処理開始"
ls /not/exist/path # ← エラーになるけど…
echo "処理続行" # ← 実行されてしまう
例:set -eあり
set -e
echo "処理開始"
ls /not/exist/path # ← ここでスクリプトが止まる!
echo "処理続行" # ← 実行されない

つけておくことで思わぬ実行を避けバグの温床を減らすのに役立ってくれます。
気をつけないといけないのは

if ! コマンド; then

のような「失敗を前提とした処理」がある場合は使えないので注意する必要があります。

set -u : 未定義の変数を使ったらエラー!

set -u

これは未定義の変数を使おうとしたらスクリプトを即エラーにするオプションです。

例:set -uなし
echo "こんにちは、$NAMEさん" # ← $NAME は未定義 → 空文字になる
例:set -uあり
set -u
echo "こんにちは、$NAMEさん" # ← ここでエラー!スクリプトが止まる

タイポや設定もれに気づきやすくなります。
${VAR:-default}のようなデフォルト値指定と組み合わせるといいですね。

set -o pipefail : パイプの中で失敗があったら検知!

set -o pipefail

これはパイプ(|)を使った時に、左側のコマンドが失敗してもちゃんと終了コードを検知できるようにするオプションです。

例:set -o pipefailなし
false | grep something # ← `false` が失敗しても、`grep` が成功すればOK扱い
echo $? # => 0(あれ?)
例:set -o pipefailあり
set -o pipefail
false | grep something # ← `false` の失敗が伝播して、全体が失敗扱い
echo $? # => 1(正しく検知!)

これで連結された処理のエラーをちゃんと見つけることができます。
例えばログをフィルタしたりするときのエラーの見落としなんかを防ぐことができますね。

なので、

set -euo pipefail

とまとめて、一行で全部のオプションをONにできます。

シェルスクリプトは便利なんですが、ちょっとしたことで壊れて思った処理をしてくれないことがあります。
だからこそ、スクリプトの一番上に `set -euo pipefail` を入れておくだけで、思わぬトラブルをぐっと減らすことができます。

基本的なことが少しでも安心をくれます。
勉強しよう。頑張りますよ。

読んでくれてありがとうございました。

それではまた!