シェルスクリプト(bash)はコマンドが失敗しても次のコマンドを実行してしまうので怖い→set -eしておけという話はよくあるが、実際どこまで有効なのか。
基本
コマンドの戻り値が0になった時にシェルを終了する
#!/bin/bash set -e echo "before false" false echo "after false"
./basic.sh before false # falseで0以外を返したので、シェルが終了しechoが実行されない
if
ifの条件が0以外を返してもシェルは終了しない
#!/bin/bash set -e echo "before if" if [ 1 = 0 ]; then echo "in if" fi echo "after if"
$ ./if.sh before if after if # ifの条件は0以外を返しているがシェルは終了していない
while,until
whileやuntilの条件が0以外を返してもシェルを終了しない
#!/bin/bash set -e i=0 echo "start while" while [ $i -lt 5 ] do echo $i i=$((i+1)) done echo "end while" echo "start until" until [ $i -lt 0 ] do echo $i i=$((i-1)) done echo "end until"
$ ./while.sh start while 0 1 2 3 4 end while # i=5の時にwhileの条件が0以外を返すがシェルは終了していない start until 5 4 3 2 1 0 end until # i=-1の時にuntilの条件が0以外を返すがシェルは終了していない
&&や||
&&や||の前のコマンドが0以外を返していてもシェルを終了しない
#!/bin/bash set -e echo "start and or" rm hoge || echo hoge rm fuga && echo fuga echo "end and or"
$ ./andor.sh start and or rm: hoge: No such file or directory hoge # rm hogeが0以外を返していても後ろのコマンドも実行される rm: fuga: No such file or directory end and or # rm fugaが0以外を返していてもシェルは終了していない
パイプライン
パイプラインの途中で0を返しても、最後まで実行される。
$ cat pipe.sh #!/bin/bash set -e echo "hoge" | grep fuga | wc -l echo ${PIPESTATUS[*]}
./pipe.sh 0 # grep fugaが0以外を返していてもwc -lが実行されている 0 1 0 # パイプラインの各コマンドのステータス
シーケンス(;)
セミコロンで繋がれたコマンドの連続の場合は途中で0を返せばシェルを終了する
#!/bin/bash set -e echo "before seq"; rm hoge; echo "after seq";
./sequence.sh before seq rm: hoge: No such file or directory # rm hogeが0以外を返したのでechoの前にシェルが終了した
サブシェル
サブシェルもset -eが有効になる。以下の場合は、rm hogeでサブシェルが終了し、その結果(...)が0以外を返して親のシェルも終了する。
#!/bin/bash set -e echo "before subshell" (rm hoge; echo "in subshell") echo "after subshell"
$ ./subshell.sh before subshell rm: hoge: No such file or directory
関数
関数の内部で0以外を返していれば、関数の途中でシェルを終了する。
#!/bin/bash hoge() { false echo "in function" } set -e echo "before function hoge" hoge echo "after function hoge"
$ ./function.sh before function hoge # hoge内のfalseで0以外を返したのでシェルが終了した
まあman見ればよかったのだけども。
$ bash --version GNU bash, バージョン 4.3.11(1)-release (x86_64-pc-linux-gnu) ... $ man bash ... -e Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero sta‐ tus. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each sub‐ shell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.
mac os xのbashのmanはmanが古いのか嘘が書いてあった。パイプラインやサブシェルの話は書いていない
$ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15) Copyright (C) 2007 Free Software Foundation, Inc. $ man bash ... -e Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a && or || list, or if the command's return value is being inverted via !. A trap on ERR, if set, is executed before the shell exits.