Edited at

Bash の set -e で即座に終了する場合・しない場合

More than 1 year has passed since last update.

Bash で set -e を設定しているときに,それによってスクリプトの実行が即座に終了する・しないを色んなケースで例示してまとめてメモしておくという,私以外の誰も得をしないことが決定的に明らかなエントリ.


このエントリにおける約束事

以下で参照している実行例,およびドキュメントの記述は2018年8月4日現在(本エントリ最終更新日)の最新である Bash 4.4 を対象にしている.

以下において,「コマンドが成功する」とは「コマンドがexit status 0 で終了すること」,「コマンドが失敗する」とは「コマンドが0以外の exit status で終了すること」とする.

以下の実行出力結果において, Did not exit immediately by `set -e'. が表示されている場合は set -e による終了が起こらなかった場合,それ以外は set -e による終了が起こった場合を表すものとする.

斜字体は Bash のドキュメント中で特別の意味を持つ用語として用いられているものを指す.

- pipeline: https://www.gnu.org/software/bash/manual/html_node/Pipelines.html

- list: https://www.gnu.org/software/bash/manual/html_node/Lists.html


pipeline 中の最後のコマンド以外で失敗が起きても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


set -e

false | echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

The right command.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/CJ7ZLWfRg4xJDpUn


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



set +o pipefail (デフォルト)の場合, pipeline の最後のコマンド以外で失敗が起きても pipeline の実行終了後に set -e による終了は起こらない.

set +o pipefail の場合, pipeline の最後のコマンドの exit status が pipeline の exit status となる仕様の結果である.


set -e

false | cat
echo
"Did not exit immediately by \`set -e'."


出力

Did not exit immediately by `set -e'.


https://wandbox.org/permlink/D3FCYurtjZqxVmtt


対応するドキュメントの記述


3.2.2 Pipelines

...

The exit status of a pipeline is the exit status of the last command in the pipeline, unless the pipefail option is enabled (see The Set Builtin).



set -o pipefail の場合, pipeline のいずれかのコマンドが失敗すると pipeline の実行終了後に set -e による終了が起きる.

set -o pipefail の場合, pipeline 中のいずれかのコマンドが失敗していると,失敗しているコマンドのうち最右のものの exit status が pipeline の exit status となる仕様の結果である.


set -e

set -o pipefail
false | cat
echo
"Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/whyyqSFXrOVDa6DI


対応するドキュメントの記述


4.3.1 The Set Builtin

...

If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully. This option is disabled by default.



pipeline の最後のコマンドが失敗した場合, pipeline の実行終了時に set -e によって終了する.

pipeline の exit status に関する仕様の結果であり, set +o pipefail であるか set -o pipefail であるかによらない.


set -e

true | false
echo
"Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/VOM2pEUqiZdmUQ3W


コマンドの前に ! が付いている場合, set -e による終了は起こらない.


例1

コマンドが成功の場合, exit status が非0であるにも関わらず set -e による終了が起こらない例となる.

set -e

! true
echo
"Did not exit immediately by \`set -e'."


出力

Did not exit immediately by `set -e'.


https://wandbox.org/permlink/WL6nEW7KCVEC8DpW


例2

set -e

! false
echo
"Did not exit immediately by \`set -e'."


出力

Did not exit immediately by `set -e'.


https://wandbox.org/permlink/6XvPOwU408Xmbv1Q


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



; の左側のコマンドが失敗した場合,右側のコマンドを実行する前に set -e によって終了する.

コマンドの前に ! を伴う場合,および whileuntilif の条件においては例外となる.各々の例外に関しては対応する項目を参照のこと.


set -e

false; echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/BCVVUW2USsBWv8YY


& の左側のコマンドが失敗しても set -e による終了は起こらない.

コマンドを background で起動した場合の exit status が 0 であることによる.コマンドの実際の exit status は wait buildin で取得でき,それによる set -e による終了の挙動は wait コマンドのそれである.


set -e

false & echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

The right command.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/mzF08WaNuBqTDpZl


対応するドキュメントの記述


3.2.3 Lists of Commands

...

If a command is terminated by the control operator &, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background. The shell does not wait for the command to finish, and the return status is 0 (true). When job control is not active (see Job Control), the standard input for asynchronous commands, in the absence of any explicit redirections, is redirected from /dev/null.



&& の左側のコマンドが失敗しても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


set -e

false && echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

Did not exit immediately by `set -e'.


https://wandbox.org/permlink/qsMh2m7avv9e74T9


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



|| の左側のコマンドが失敗しても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


set -e

false || echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

The right command.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/nHhG9vmbF5vqyetW


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



list の最後のコマンドで & が付いていないものが実行され,その結果が失敗の場合, set -e によって終了する.

list の最後のコマンドが実行された場合,その exit status が list の exit status となる仕様による.


例1

set -e

echo 'The left command.'; false
echo
"Did not exit immediately by \`set -e'."


出力

The left command.


https://wandbox.org/permlink/v0EyqASKiWb7U2HR


例2

set -e

echo 'The left command.' & false
echo
"Did not exit immediately by \`set -e'."


出力

The left command.


https://wandbox.org/permlink/Vbsqibfm2BIfcley


例3

set -e

true && false
echo
"Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/7YwkpBfFivEgslj5


例4

set -e

false || false
echo
"Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/MsIRMwyl9fBGFLqI


until の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


set -e

until false; echo 'The second command.'; false; do
echo 'A command in the loop.'
break
done
echo "Did not exit immediately by \`set -e'."


出力

The second command.

A command in the loop.
Did not exit immediately by `set -e'.

https://wandbox.org/permlink/6sKWjs28E0qm1RF7


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



while の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


set -e

while false; echo 'The second command'; false; echo 'The fourth command.'; do
echo 'A command in the loop.'
break
done
echo "Did not exit immediately by \`set -e'."


出力

The second command

The fourth command.
A command in the loop.
Did not exit immediately by `set -e'.

https://wandbox.org/permlink/0doNE2EA8fhwcjB9


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



if の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.

exit status が非0であるにも関わらず set -e による終了が起こらない例となる.


例1

set -e

if false; then
echo 'The command on success.'
else
echo 'The command on failure.'
fi
echo "Did not exit immediately by \`set -e'."


出力

The command on failure.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/o63pKdzHIJrCJdtM


例2

set -e

if false; true; then
echo 'The command on success.'
else
echo 'The command on failure.'
fi
echo "Did not exit immediately by \`set -e'."


出力

The command on success.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/9dX6zu4nsokC6L8j


対応するドキュメントの記述


4.3.1 The Set Builtin

...

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 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 status is being inverted with !.



( list ) における set -e による終了の挙動は, list に対するものに準じる. ( list ) の中で set -e によって終了するのはサブシェルである.

サブシェルに set -e の設定が引き継がれる仕様による結果である.


例1

( list ) を実行するサブシェルが set -e によって終了し,さらに ( list ) の実行後にも set -e による終了が起きる例.

set -e

(false; echo 'The right command.')
echo "Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/hF2sBxkjckFYgehz


例2

( list ) を実行するサブシェルに対しては set -e による終了が起こらないが, ( list ) の実行後に set -e による終了が起きる例.

set -e

(false && echo 'The right command.')
echo "Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/tdlavCqfKh73ybXx


対応するドキュメントの記述


3.7.3 Command Execution Environment

...

Subshells spawned to execute command substitutions inherit the value of the -e option from the parent shell. When not in POSIX mode, Bash clears the -e option in such subshells.



{ list; } おける set -e による終了の挙動は, list に対するものに準じる.


coproc に対する set -e の挙動は & によって background で実行するコマンドに対するものに準じる.


readonly 属性を付与された関数を再定義しようとしても set -e による終了は起こらない.

対応するドキュメントの記述が見つけられなかった.従って,以下の実行例は実装固有の挙動である可能性を否定できない.


set -e

function f () { true; }
declare -fr f
function f () { true; }
echo "Did not exit immediately by \`set -e'."


出力

prog.sh: line 6: f: readonly function

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/BougSH10m9EZfwZ3


pipeline の最後以外で関数を呼び出した場合,その関数本体内では set -e による終了が起こる.

以下のドキュメントの記述と矛盾すると思うんだが…….


4.3.1 The Set Builtin

...

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.



set -e

function func () { false; echo 'The second command.'; }
func | cat
echo
"Did not exit immediately by \`set -e'."


出力

Did not exit immediately by `set -e'.


https://wandbox.org/permlink/KaKCIbsSIvpMW7Zh


set -e による終了が起こらない文脈で関数を呼び出した場合,関数本体内の任意のコマンドに関して,失敗しても set -e による終了は起こらない.


例1

set -e

function func () { false; echo 'A command after failure in a function.'; }
! func
echo "Did not exit immediately by \`set -e'."


出力

A command after failure in a function.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/pKo4g3p6Aj3ZGvk5


例2

set -e

function func () { false; echo 'A command after failure in a function.'; }
func && echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

A command after failure in a function.

The right command.
Did not exit immediately by `set -e'.

https://wandbox.org/permlink/fTCqXtFFkJMsWfoc


例3

set -e

function func () { false; echo 'A command after failure in a function.'; }
func || echo 'The right command.'
echo "Did not exit immediately by \`set -e'."


出力

A command after failure in a function.

Did not exit immediately by `set -e'.

https://wandbox.org/permlink/gdA0KQ2cLTtncQlB


例4

set -e

function func () { false; echo 'A command after failure in a function.'; }
if func; then
echo 'A command on success.'
else
echo 'A command on failure.'
fi
echo "Did not exit immediately by \`set -e'."


出力

A command after failure in a function.

A command on success.
Did not exit immediately by `set -e'.

https://wandbox.org/permlink/3mFxQblq7EJk5AAV


対応するドキュメントの記述


4.3.1 The Set Builtin

...

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.



command substitution のコマンドが失敗しても set -e による終了は起こらない.

command substitution が variable assignment の右辺に出現する場合(以下を参照)を除く.


set -e

echo $(false)
echo "Did not exit immediately by \`set -e'."


出力


Did not exit immediately by `set -e'.

https://wandbox.org/permlink/gdOYA3AwScpIOTA0


variable assignment の右辺に出現する command substitution が失敗した場合, variable assignment が失敗となり set -e によって終了する.


set -e

var=$(false)
echo "Did not exit immediately by \`set -e'."


出力


https://wandbox.org/permlink/FNYuqtK0W3c4MdQl


対応するドキュメントの記述


3.7.1 Simple Command Expansion

...

If there is a command name left after expansion, execution proceeds as described below. Otherwise, the command exits. If one of the expansions contained a command substitution, the exit status of the command is the exit status of the last command substitution performed. If there were no command substitutions, the command exits with a status of zero.



コマンドの探索に失敗した場合,失敗したコマンドに対する set -e の挙動に準じる.


trap として指定したコマンドに対する set -e の挙動は通常のものに準じる.


例1

set -e

trap 'false; echo "Did not exit immediately by \`set -e'\''."' EXIT


出力


https://wandbox.org/permlink/eBVyG5VYINAzJ6bF


例2

set -e

function func ()
{
trap 'false; echo "Did not exit immediately by \`set -e'\''."' RETURN
}
func
echo 'A command after function return.'


出力


https://wandbox.org/permlink/sRfMdKrt2WPUMPF5

  • set +o pipefail (デフォルト)の場合, pipeline の最後のコマンド以外で失敗が起きても pipeline の実行終了後に set -e による終了は起こらない.
  • set -o pipefail の場合, pipeline のいずれかのコマンドが失敗すると pipeline の実行終了後に set -e による終了が起きる.
  • pipeline の最後のコマンドが失敗した場合, pipeline の実行終了時に set -e によって終了する.
  • コマンドの前に ! が付いている場合, set -e による終了は起こらない.
  • ; の左側のコマンドが失敗した場合,右側のコマンドを実行する前に set -e によって終了する.
  • & の左側のコマンドが失敗しても set -e による終了は起こらない.
  • && の左側のコマンドが失敗しても set -e による終了は起こらない.
  • || の左側のコマンドが失敗しても set -e による終了は起こらない.
  • list の最後のコマンドで & が付いていないものが実行され,その結果が失敗の場合, set -e によって終了する.
  • until の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.
  • while の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.
  • if の条件に現れる任意のコマンドに関して,失敗しても set -e による終了は起こらない.
  • ( list ) における set -e による終了の挙動は, list に対するものに準じる. ( list ) の中で set -e によって終了するのはサブシェルである.
  • { list; } おける set -e による終了の挙動は, list に対するものに準じる.
  • coproc に対する set -e の挙動は & によって background で実行するコマンドに対するものに準じる.
  • readonly 属性を付与された関数を再定義しようとしても set -e による終了は起こらない.
  • pipeline の最後以外で関数を呼び出した場合,その関数本体内では set -e による終了が起こる.
  • set -e による終了が起こらない文脈で関数を呼び出した場合,関数本体内の任意のコマンドに関して,失敗しても set -e による終了は起こらない.
  • command substitution のコマンドが失敗しても set -e による終了は起こらない.
  • variable assignment の右辺に出現する command substitution が失敗した場合, variable assignment が失敗となり set -e によって終了する.
  • コマンドの探索に失敗した場合,失敗したコマンドに対する set -e の挙動に準じる.
  • trap として指定したコマンドに対する set -e の挙動は通常のものに準じる.