はじめに
やらなくても良いことならやらない1、nikkieです。
今回は直近で知った、シェルスクリプト関連のアウトプットです。
目次
- はじめに
- 目次
- まとめ(バックアップしておく気付き)
- 今回のサンプルシェルスクリプト(開始状態)
- exit
- set -e
- trap
- これらを調べた背景
- 終わりに
- (2023/05/02 追記)trapと一時ファイルを教えていただく
まとめ(バックアップしておく気付き)
今回のサンプルシェルスクリプト(開始状態)
#!/usr/bin/env bash echo "かがみの孤城、みんな見て!" echo "こころちゃんかわいい"
スクリプト名はexample.sh
。
実行権限を付けてから実行します。
$ ./example.sh かがみの孤城、みんな見て! こころちゃんかわいい
exit
シェルスクリプトにexitを書くと、そこでスクリプトを終了します。
以降のコマンドは実行されません。
#!/usr/bin/env bash echo "かがみの孤城、みんな見て!" exit echo "こころちゃんかわいい"
exit
の後の終了コードの指定を省略すると0
(正常終了)です2。
$ ./example.sh かがみの孤城、みんな見て! $ echo $? 0
異常終了を表すステータスコードを渡す例です:
#!/usr/bin/env bash echo "かがみの孤城、みんな見て!" - exit + exit 1 echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! $ echo $? 1
関数内のexitもそこでスクリプトを終了
関数を導入します。
#!/usr/bin/env bash function awesome_function() { echo "Start function with $1" echo "End function" } awesome_function 42 exit 1 echo "こころちゃんかわいい"
awesome_function 42
と関数を呼び出しました。
その後のexit 1
でスクリプトを終了します。
$ ./example.sh Start function with 42 End function
では関数内でexit
したらどうなるでしょうか?
#!/usr/bin/env bash function awesome_function() { echo "Start function with $1" + exit $1 echo "End function" } awesome_function 42 exit 1 echo "こころちゃんかわいい"
$ ./example.sh Start function with 42 $ echo $? 42
関数内のexit
によりシェルスクリプト自体が終了しました!
exit
を含む関数は、シェルスクリプトを制御することになるわけですね。
小まとめ:シェルスクリプトのexit
- シェルスクリプトを終了する
- 終了コードを指定できる
- 関数の外でも中でもどこで実行されても、その行でシェルスクリプトを終了
- (シェルスクリプトにスコープという概念があるのか不明だが、"スコープによらない"と理解した)
『マスタリングLinuxシェルスクリプト 第2版』1.6.1より
exitはシェルの組み込みコマンドであり、スクリプトを終了するために使われます。
終了ステータスを、整数の引数として指定します。
set -e
シェルスクリプトは途中のコマンドでエラーが送出されても、そのまま動き続けます。
#!/usr/bin/env bash echo "かがみの孤城、みんな見て!" # 存在しないファイルのls(故意にエラー送出) ls spam echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! ls: spam: No such file or directory こころちゃんかわいい $ echo $? 0
コマンドでエラーが送出された際、直ちにスクリプトを止める指定がset -e
です。
#!/usr/bin/env bash + set -e echo "かがみの孤城、みんな見て!" # 存在しないファイルのls(故意にエラー送出) ls spam echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! ls: spam: No such file or directory $ echo $? 1
「こころちゃんかわいい」が出力されていませんね3。
スクリプトの終了コードは、最後に実行されたls
コマンドの終了コード1(No such file or directory)となっています。
小まとめ:シェルスクリプトのset -e
『マスタリングLinuxシェルスクリプト 第2版』A.1.7.2より4
-eオプションを付けてsetコマンドを実行するとそれ以降にスクリプト中のコマンドでエラーが発生した場合(コマンドの終了ステータスが0でない場合)にスクリプトの実行を終了します。
trap
trap
でエラー送出時に呼び出されるハンドラを指定できることを知りました。
#!/usr/bin/env bash set -e function error_handler() { echo "エラーハンドラです" } trap error_handler ERR echo "かがみの孤城、みんな見て!" # 存在しないファイルのls(故意にエラー送出) ls spam echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! ls: spam: No such file or directory エラーハンドラです $ echo $? 1
ls
コマンド実行でエラーが送出されると、エラーハンドラを実行してから終了という動きをします。
trap
で指定したエラーハンドラを実行- エラーが送出されているので、
set -e
指定によりスクリプトを終了
小まとめ:シェルスクリプトのtrap
『マスタリングLinuxシェルスクリプト 第2版』A.1.8より
trapコマンドを使用する際は、最初の引数に実行したいコマンドを指定し、2番目以降の引数にコマンドを実行させたいシグナル名またはシグナル番号を指定します。
ERRとは特別なシグナル名の1つ
ERR(コマンド実行時にエラーが発生した場合)
trap
コマンドの入り口となったのはこちらのエントリです:
trap コマンドを使ったシェルスクリプトのエラーハンドリング - CUBE SUGAR CONTAINER
これらを調べた背景
この3つについて調べたのは、エラーハンドラでexit
しているスクリプトを読み解きたかったからです。
#!/usr/bin/env bash set -e function error_handler() { error_status=$? echo "エラーハンドラです" exit $error_status echo "エラーハンドラ終了" } trap error_handler ERR echo "かがみの孤城、みんな見て!" # 存在しないファイルのls(故意にエラー送出) ls spam echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! ls: spam: No such file or directory エラーハンドラです $ echo $? 1
exit
、set -e
、trap
について理解していった結果、エラーハンドラでのexit
は不要と考えています。
その理由は、エラーハンドラでexit
しなくてもset -e
で止まるからです。
#!/usr/bin/env bash set -e function error_handler() { error_status=$? echo "エラーハンドラです" - exit $error_status echo "エラーハンドラ終了" } trap error_handler ERR echo "かがみの孤城、みんな見て!" # 存在しないファイルのls(故意にエラー送出) ls spam echo "こころちゃんかわいい"
$ ./example.sh かがみの孤城、みんな見て! ls: spam: No such file or directory エラーハンドラです エラーハンドラ終了 $ echo $? 1
シェルスクリプトの知見が豊富ではない(勉強中)ので間違っている可能性もありますが、エラーハンドラがexit
する(シェルスクリプトを制御する)のは、責務が大きすぎるように感じます。
なので、set -e
があるならば、trap
で設定するエラーハンドラでは(やらなくてもいい)exit
は書かないというのが現時点の私の考えです。
終わりに
set -e
とtrap
があれば、エラーハンドラ内でexit
は不要!
よく分からないコマンドが複数ありましたが、1つ1つ理解していったことで、簡潔な実装ができることに気付けました。
「急がば回れ」とはこのことですね。
そして、『マスタリングLinuxシェルスクリプト 第2版』、これはいい本ですね!5
今まで必要な箇所をWebで調べてシェルスクリプトを書いてきましたが、体系的に整理されていて、理解のムラがならされそうです。
(2023/05/02 追記)trapと一時ファイルを教えていただく
arterminalさん、ありがとうございます!
setとtrapは重要ですよね!
— NP (@arterminal) 2023年1月26日
私はsetは専ら-euの形式で使います。-uで未定義変数の参照を検出してくれるので便利😋
また記事の主旨から外れますが、trapは一時ファイルを作成したときの削除に有用です。mktempで一時ファイルを作成してEXITシグナルによるtrapでゴミ箱にポイッ🤗
情報、誠にありがとうございます!
— nikkie にっきー (@ftnext) 2023年1月26日
この記事では取り上げませんでしたが、set -uもいつも助けられてます。便利です😃
trapで一時ファイル削除する方法は初めて知ったのですが、これは頭いいですね!👏
シグナルによるトラップでこんなこともできるなんて…!
-
『氷菓』の折木奉太郎のモットーです。
↩《📷「#氷菓」今日の1枚》
— TVアニメ「氷菓」10周年記念フィルムコンサート【公式】@アーカイブ公開中! (@Hyouka_10th) 2022年11月19日
やらなくても良いことならやらない。
やらなければいけないことなら手短にーーー
#1 「伝統ある古典部の再生」より
🔻#氷菓音楽会 チケット最速先行中!🎻https://t.co/b29xGyEKDW
(応募締切は11/21 23:59です⏰) pic.twitter.com/E0sTeIy0xh -
$?
は直前のコマンドの終了コードを確認できるという理解です↩ - この例にした自分が悪いのですが、「こころちゃんかわいい」が出力できなくて残念です↩
-
A.1.7.3によると、
set -E
とすることで、関数内のエラーに対してもtrap
コマンドで指定したハンドラが有効になるようです↩ - こちらのエントリでよさそうと思いました:『マスタリングLinuxシェルスクリプト 第2版』、こういう1冊手元に有るとずっと使える本はちゃんと買っておきたいですね - Magnolia Tech↩