LoginSignup
27
13

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-08-03

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'.

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

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'.

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

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'."
出力

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

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'."
出力

コマンドの前に ! が付いている場合, 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'.

例2

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

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

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'."
出力

& の左側のコマンドが失敗しても 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'.

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

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'.

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

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'.

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

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.

例2

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

例3

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

例4

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

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'.

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

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'.

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

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'.

例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'.

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

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'."
出力

例2

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

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

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

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'.

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'.

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'.

例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'.

例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'.

例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'.

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

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'.

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

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

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

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
出力

例2

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

27
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
13