Corredor

ウェブ、プログラミングの勉強メモ。

Bash シェルスクリプトを安全に実行するための便利な set コマンド

Jenkins で実行するシェルスクリプトを書いていて、

  1. sh ./script.sh で実行すると実行されるコマンドが Jenkins のコンソールに出力されないから分かりづらいな
  2. コーディングミスとかでエラーになった時は中断してほしいな
  3. コーディングミスで未定義の変数を使ってたりしたら教えてほしいな
  4. というか実行前に Lint チェックみたいなことできなのかな

と思ったので調べてみたところ、set コマンドの各種オプションがそれぞれの要望を叶えてくれることが分かった。

そこで今回は、set コマンドの便利オプションと使い方を紹介する。

set -x : シェルスクリプト内のコマンドをコンソール出力する

まず set -x から。コレをシェルスクリプトの先頭に書いておく。

#!/bin/bash

set -x

echo ほげふが

この状態で $ sh コマンドを叩くと、以下のように出力される。

$ sh script.sh
+ echo $'?\201??\201\222?\201??\201\214'
ほげふが

日本語部分は数値参照になったりするが、だいたい何のコマンドを叩いたか分かるので助かる。

set -v : 実行するコマンドそのものを出力する

set -x に似たオプションが、set -v。こちらのオプションは、変数などを展開せず出力してくれる。

#!/bin/bash

set -v

MY_VARIABLE='Hoge'
echo $MY_VARIABLE

こんなファイルがあったとして、次のように出力される。

$ sh script.sh

MY_VARIABLE='Hoge'
echo $MY_VARIABLE
Hoge

echo $MY_VARIABLE 部分が分かりやすいだろう。変数展開せず、コマンドをそのまま出力している。

シェルスクリプトの set -v 部分を set -xv もしくは set -vx (順不同) とすることで、set -x オプションとの併用もできる。

$ sh script.sh

MY_VARIABLE='Hoge'
+ MY_VARIABLE=Hoge
echo $MY_VARIABLE
+ echo Hoge
Hoge

+ から始まる行が set -x によるモノ。set -v によるコマンド出力なのか、コマンド実行結果の標準出力なのかが一見分かりづらいものの、充分にデバッグできるだろう。

set -e : エラー時にシェルスクリプトを中断する

お次は set -e。まずはこのオプションを指定せず、以下のようなシェルスクリプトを用意してみる。

#!/bin/bash

cat none.txt
echo ほげふが

ココで、cat で出力しようとしている none.txt というファイルが存在しないとする。通常であれば、以下のような実行結果になる。

$ sh script.sh
cat: none.txt: No such file or directory
ほげふが

cat コマンドが正常に動かず、その後に書かれた echo コマンドも実行される。

ココに set -e コマンドを追加してみよう。

#!/bin/bash

set -e

cat none.txt
echo ほげふが

そしてシェルスクリプトを実行してみると、以下のようになる。

$ sh script.sh
cat: none.txt: No such file or directory

cat でエラーが出ると、後続の echo コマンドが実行されていないことが分かる。シェルスクリプトがエラーの時点で中断されているのだ。

もし、エラーが出る場合が想定済みで、エラーを無視したい場合は、次のように || true とすれば誤魔化せる。

cat none.txt || true

また、-e オプションを一時的に無効化することもできる。コレについては後述。

set -u : 未定義の変数が使用された時にシェルスクリプトを中断する

今度は set -u。コチラは未定義の変数を使用した時にシェルスクリプトを中断してくれる。

#!/bin/bash

echo ほげ
echo $NONE_VARIABLE
echo ふが

例えばこんなコードがあり、$NONE_VARIABLE という変数が未定義だとする。このままだと、

$ sh script.sh
ほげ

ふが

というように、未定義の $NONE_VARIABLE を空文字のように扱い、処理が継続してしまう。

コレに対し、

#!/bin/bash

set -u

echo ほげ
echo $NONE_VARIABLE
echo ふが

set -u コマンドを設定すると、以下のようになる。

$ sh script.sh
ほげ
script.sh: line 6: NONE_VARIABLE: unbound variable

最後の echo ふが は実行されておらず、処理がそこで中断されていることが分かる。

set -n : コマンドを実行せず構文チェックのみを行う

シェルスクリプトの挙動を変更するオプションとしては最後に紹介するモノ。set -n は、シェルスクリプト内のコマンドは実行せず、構文チェックのみ行ってくれる。

例えば以下のような、if コマンドの書き方が間違っているスクリプトを用意する。

#!/bin/bash

set -n

echo スタート
if[true]; then
  echo HOGE
fi
echo おわり

シェルスクリプトはコマンドの羅列であり、if[] はそれぞれ別のコマンドであるから、本来はスペースで区切らないといけないのだが、うっかりスペースを入れずに繋げて書いてしまっている。

コレを実行してみると、

$ sh script.sh
script.sh: line 6: syntax error near unexpected token `then'
script.sh: line 6: `if[true]; then'

と、このように echo などコマンドは一切実行されず、構文チェックのみ行われている。

コーディング中はこのオプションを付与して構文誤りがないかチェックし、実装が完了したら set -n コマンドを除去して実際に動作するようにしておこう。

set -o : set コマンドでの設定状況を知る

ココからは set コマンドの活用編。ターミナルで $ set -o と叩くと、これら set コマンドのどのオプションが有効・無効になっているか確認できる。こんな感じだ。

$ set -o
allexport       off
braceexpand     on
emacs           on
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      on
history           on
ignoreeof       off
interactive-comments    on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

set + : 設定したオプションを無効に戻す

ココまで、set -vset -e のように、ハイフン - でオプションを指定して、そのオプションを有効にしてきたが、これらのオプションを一時的に無効にしたい場合もあるだろう。

その際は、set +v とか set +o のように、プラス + 記号でオプションを指定すると、そのオプションを無効化できる。

#!/bin/bash

# エラー時に中断するようにする
set -e

echo ほげほげ

# 以下のコマンドでエラーが起きても、ココだけは無視して継続させたい
# そんな時は set +e でオプションを解除する
set +e

cat none.txt

# また元に戻す
set -e

echo おわり

こんな感じ。

当然だが、set コマンドはシェルスクリプト内でなくとも、ターミナル上でも使用できるので、先程の set -o オプションを使って設定状況を見比べてみよう。

# 設定確認
$ set -o | grep xtrace
xtrace          off       # off の状態

# x オプションを設定する (set -o xtrace と書いても良い)
$ set -x
++ update_terminal_cwd
++ local url_path=
++ local i ch hexch LC_CTYPE=C LC_ALL=
# 省略……

# 設定確認
$ set -o | grep xtrace
+ set -o
+ grep --color xtrace
xtrace          on       # on になっている
++ update_terminal_cwd
++ local url_path=
++ local i ch hexch LC_CTYPE=C LC_ALL=
# 省略……

# x オプションを切る (set +o xtrace と書いても良い)
$ set +x
+ set +x

$ set -o | grep xtrace
xtrace          off       # off に戻っている

このように、オプションの On・Off ができている。

Shebang でセットする書き方がうまくいかなかった

この set コマンドと同等のオプションを、Shebang のところに書いても良いかのように読み取れる文献もあったのだが、自分が試した限りだと上手くいかなかった。

#!/bin/bash -xe

# ↑ こんな風に設定できるらしいが、有効にならず。

仕方がないので、自分は

#!/bin/bash
set -xe

と、Shebang の直後に set コマンドを書く運用にする。

まとめ

  • 実装中に構文チェックしたい (コマンドは実行されない)
    • set -n
  • スクリプト実行時に表示されるコマンドを見たい
    • set -x (行頭に + 記号が付き、変数展開される)
    • set -v (変数展開されない)
  • スクリプト実行時にエラーがあったらその時点で中断したい
    • set -e (終了コードが 0 でないコマンドが出たら中断する)
    • set -u (未定義の変数が出てきたら中断する)
  • 個人的によく使う組み合わせ
    • set -xeu

以上。set コマンドでシェルスクリプトがとても安全・便利に書けるようになった。

新しいシェルプログラミングの教科書

新しいシェルプログラミングの教科書