例えば以下のようなコードで、移動先のディレクトリがなかったらhelloはechoされないとわたしは思っていたのですが、実際のところはhelloまで出でしまいます。
#!/bin/sh cd hoge echo hello
↓出力
sample.sh: line 3: cd: hoge: No such file or directory hello
そもそもshellはエラーが出たことを認識できているのでしょうか?
コマンドの成否を確認する
終了ステータス | UNIX & Linux コマンド・シェルスクリプト リファレンス
コマンド終了時には「終了ステータス (exit-status)」と呼ばれるコマンドの成否を表す数値が特殊変数
$?
に自動で設定される。
終了ステータスとして設定される値は一般的には
- 成功:0
- 失敗:1 (コマンドやエラーの種類によっては 0 以外)
が設定されるそうです。
ということなのでエラーを認識できているのか確認は以下で行います。
#!/bin/sh cd hoge echo $? echo hello echo $?
↓出力
sample.sh: line 3: cd: hoge: No such file or directory 1 hello 0
どうやらエラーであることは認識できているようです。 ならばtry-catchしたいということでshellでtry-catchする方法を調べてみました。
shellでtry-catch
shell try-catch
で検索したらこちらのサイトが見つかりました。
シェルスクリプトでtry-catch-finally - Qiita
こちらのサイトによるとtry-catchするなら以下のように書けば良いことがわかりました。
#!/bin/sh set -eu function catch { echo Catch } function finally { echo Finally } trap catch ERR trap finally EXIT cd hoge echo hello
これを出力すると以下になりました。
sample.sh: line 13: cd: hoge: No such file or directory Catch Finally
先ほどまで出力されていたhello
の文字が出力されなくなり、Catch、Finallyの文字が出力されたことから、cd hoge
でエラーが発生したタイミングで以降の処理がスキップされ、trapで設定したcatch、finallyを通過して終了したことがわかります。
同様に成功するパターンも確認しておきましょう。
#!/bin/sh set -eu function catch { echo Catch } function finally { echo Finally } trap catch ERR trap finally EXIT cd . echo hello
この出力は以下のようになりました。
hello Finally
以上より、trap
およびset -eu
によってどういうわけかtry-catchができるようになりました。
set -euとは
shellのコマンドのset
とそのオプションであるe
とu
からなっているように見えるのでshellのsetについて調べてみました。見つかったのがこちらです。
【 set 】コマンド――シェルの設定を確認、変更する:Linux基本コマンドTips(205) - @IT
まずはset
というコマンドはshellの設定の確認、変更を行うコマンドとなっています。
そして、e
とu
は以下のように説明されています。
option | option 名 | 説明 |
---|---|---|
e | errexit | パイプやサブシェルで実行したコマンドが1つでもエラーになったら直ちにシェルを終了する |
u | nounset | パラメーター展開中に、設定していない変数があったらエラーとする(特殊パラメーターである「@」と「*」は除く) |
今回エラー発生タイミングで以降の処理をスキップをするようになったのは、set -e
のおかげであるということですね。
ちなみにset -u
の恩恵を受けるようなコードは以下になります。
#!/bin/sh set -u $hoge echo hello
↓出力
sample.sh: line 5: hoge: unbound variable
hogeという設定していない変数を利用しようとした結果エラーになりました。この設定によるエラーはe
のオプションなしでも以降の処理はスキップして終了するのですね。念の為オプションなしの方も確認しておきましょう。
#!/bin/sh $hoge echo hello
↓出力
hello
エラーが吐かれることなく最後まで実行していました。
trapとは
trap [ action condition ... ]
という構文からなっています。
trap ユーティリティーは、 condition が発生したときに qsh によって実行される action を設定します。 qsh は、trap を実行しているときに action を展開し、condition が発生したときに再度展開します。 また、trapには
l
,p
というオプションがあり、それぞれ以下のようにことができます。
option | 説明 |
---|---|
l | すべてのシグナル名とその対応する番号のリストを表示します。 |
p | 各トラップを再入力可能な形式で表示します。 |
conditionにはシグナルの名または番号を指定します。有効なシグナルのリストはtrap -l
で参照することができるようです。
また、デフォルトで存在するのは以下です。
condition | 説明 |
---|---|
ERR | エラー時に(コマンドの返り値が0以外)の時に実行 |
EXIT (0) | 終了時に実行 |
DEBUG | 各種コマンドの後で実行 |
まとめ
shellでも他のプログラミング言語でできることはできるんだなあと思いました。