はじめに
シェルスクリプトは手軽に記述できますが、想定外の挙動やエラーに悩まされることもあります。
今回は、安全なオプション設定と、トラブルシューティングに役立つデバッグ方法について解説します。
1. set オプション
スクリプトの冒頭に set -e などを見かけたことはないでしょうか。
幾つかのオプションがあるので、以下で、それぞれの意味と副作用を解説します。
set -e (errexit)
コマンドがエラー(ゼロ以外の終了ステータス)を返した場合に、スクリプトを即終了させます。
予期せぬエラーが発生した状態で処理が続行されるのを防ぐために使います。
ただし、grep のように「検索結果がない場合に終了ステータス 1 を返す」コマンドなどでは注意が必要です。
set -e
# grep は、マッチしない場合に終了ステータス 1 を返すため、ここでスクリプトが終了してしまう
echo "hello" | grep "world"
echo "This line is never executed"
回避策として、|| true を使うか、if 文の中で実行します。
echo "hello" | grep "world" || true
set -u (nounset)
未定義の変数を参照した場合にエラーとして扱います。
タイプミスによるバグを防ぐために使います。
set -u
echo $1 # 引数がないとエラーになる
echo ${1:-} # デフォルト値を指定すればエラーにならない
set -o pipefail
通常、パイプラインの終了ステータスは「最後のコマンド」のものが採用されますが、このオプションを有効にすると、パイプラインの途中で失敗したコマンドがあれば、その終了ステータスが返されるようになります。
# set -o pipefail を有効にする
set -o pipefail
# 存在しないコマンドを実行する
# pipefail が有効なので、パイプライン全体がエラー終了ステータスを持つ
non_existent_command | wc -l
echo $? # -> 127 (non_existent_command のエラーコード)
2. デバッグ
実行トレースの表示 set -x
スクリプトの実行内容を表示したい場合、set -x オプションを使用します。
変数がどのように展開されたかを確認できるため、デバッグ時に非常に便利です。
set -x
result=$(ls -l | wc -l)
echo "Count: $result"
実行すると、以下のように + 付きで実行されたコマンドが表示されます。
+ ls -l
+ wc -l
+ result=5
+ echo 'Count: 5'
Count: 5
構文チェック bash -n / zsh -n
スクリプトを実行せずに、構文エラー(if の閉じ忘れなど)だけをチェックしたい場合は -n オプションを使用します。
bash -n script.sh
# または
zsh -n script.sh
何も表示されなければ、構文上のエラーはありません。
3. シェルスクリプトのベストプラクティス
シェバン (Shebang) の指定
スクリプトの1行目には、どのシェルで実行するかを明示する シェバン を記述しましょう。
#!/bin/bash
# または
#!/bin/zsh
移植性を高めるために #!/bin/sh を使うこともありますが、その場合はBashやZsh固有の機能(配列やプロセス置換など)が、使えなくなる可能性があります。
次回
次回は 「パフォーマンスチューニングとワンライナー」 について解説します。