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

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` による終了が起こらない例となる．

#### 例
```bash
set -e
false | echo 'The right command.'
echo "Did not exit immediately by \`set -e'."
```
```text:出力
The right command.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/CJ7ZLWfRg4xJDpUn

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> 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 となる仕様の結果である．

#### 例
```bash
set -e
false | cat
echo "Did not exit immediately by \`set -e'."
```
```text:出力
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/D3FCYurtjZqxVmtt

#### 対応するドキュメントの記述
> [3.2.2 Pipelines](https://www.gnu.org/software/bash/manual/html_node/Pipelines.html)
> ...
> 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 となる仕様の結果である．

#### 例
```bash
set -e
set -o pipefail
false | cat
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/whyyqSFXrOVDa6DI

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> **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` であるかによらない．

#### 例
```bash
set -e
true | false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/VOM2pEUqiZdmUQ3W

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

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

```bash
set -e
! true
echo "Did not exit immediately by \`set -e'."
```
```text:出力
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/WL6nEW7KCVEC8DpW

#### 例2
```bash
set -e
! false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/6XvPOwU408Xmbv1Q

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> 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` によって終了する．
コマンドの前に `!` を伴う場合，および `while`， `until`， `if` の条件においては例外となる．各々の例外に関しては対応する項目を参照のこと．

#### 例
```bash
set -e
false; echo 'The right command.'
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/BCVVUW2USsBWv8YY

# `&` の左側のコマンドが失敗しても `set -e` による終了は起こらない．
コマンドを background で起動した場合の exit status が `0` であることによる．コマンドの実際の exit status は `wait` buildin で取得でき，それによる `set -e` による終了の挙動は `wait` コマンドのそれである．

#### 例
```bash
set -e
false & echo 'The right command.'
echo "Did not exit immediately by \`set -e'."
```
```text:出力
The right command.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/mzF08WaNuBqTDpZl

#### 対応するドキュメントの記述
> [3.2.3 Lists of Commands](https://www.gnu.org/software/bash/manual/html_node/Lists.html)
> ...
> **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` による終了が起こらない例となる．

#### 例
```bash
set -e
false && echo 'The right command.'
echo "Did not exit immediately by \`set -e'."
```
```text:出力
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/qsMh2m7avv9e74T9

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> 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` による終了が起こらない例となる．

#### 例
```bash
set -e
false || echo 'The right command.'
echo "Did not exit immediately by \`set -e'."
```
```text:出力
The right command.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/nHhG9vmbF5vqyetW

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> 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
```bash
set -e
echo 'The left command.'; false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
The left command.
```
https://wandbox.org/permlink/v0EyqASKiWb7U2HR

#### 例2
```bash
set -e
echo 'The left command.' & false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
The left command.
```
https://wandbox.org/permlink/Vbsqibfm2BIfcley

#### 例3
```bash
set -e
true && false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/7YwkpBfFivEgslj5

#### 例4
```bash
set -e
false || false
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/MsIRMwyl9fBGFLqI

# `until` の条件に現れる任意のコマンドに関して，失敗しても `set -e` による終了は起こらない．
exit status が非`0`であるにも関わらず `set -e` による終了が起こらない例となる．

#### 例
```bash
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'."
```
```text:出力
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](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> **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` による終了が起こらない例となる．

#### 例
```bash
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'."
```
```text:出力
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](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> **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
```bash
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'."
```
```text:出力
The command on failure.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/o63pKdzHIJrCJdtM

#### 例2
```bash
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'."
```
```text:出力
The command on success.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/9dX6zu4nsokC6L8j

#### 対応するドキュメントの記述
> [4.3.1 The Set Builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> 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 `!`.

# <code>( <i>list</i> )</code> における `set -e` による終了の挙動は， *list* に対するものに準じる． <code>( <i>list</i> )</code> の中で `set -e` によって終了するのはサブシェルである．
サブシェルに `set -e` の設定が引き継がれる仕様による結果である．

#### 例1
<code>( <i>list</i> )</code> を実行するサブシェルが `set -e` によって終了し，さらに <code>( <i>list</i> )</code> の実行後にも `set -e` による終了が起きる例．

```bash
set -e
(false; echo 'The right command.')
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/hF2sBxkjckFYgehz

#### 例2
<code>( <i>list</i> )</code> を実行するサブシェルに対しては `set -e` による終了が起こらないが， <code>( <i>list</i> )</code> の実行後に `set -e` による終了が起きる例．

```bash
set -e
(false && echo 'The right command.')
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/tdlavCqfKh73ybXx

#### 対応するドキュメントの記述
> [3.7.3 Command Execution Environment](https://www.gnu.org/software/bash/manual/html_node/Command-Execution-Environment.html)
> ...
> **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.

# <code>{ <i>list</i>; }</code> おける `set -e` による終了の挙動は， *list* に対するものに準じる．

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

# `readonly` 属性を付与された関数を再定義しようとしても `set -e` による終了は起こらない．
対応するドキュメントの記述が見つけられなかった．従って，以下の実行例は実装固有の挙動である可能性を否定できない．

#### 例
```bash
set -e
function f () { true; }
declare -fr f
function f () { true; }
echo "Did not exit immediately by \`set -e'."
```
```text:出力
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](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> **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.**

#### 例
```bash
set -e
function func () { false; echo 'The second command.'; }
func | cat
echo "Did not exit immediately by \`set -e'."
```
```text:出力
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/KaKCIbsSIvpMW7Zh

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

#### 例1
```bash
set -e
function func () { false; echo 'A command after failure in a function.'; }
! func
echo "Did not exit immediately by \`set -e'."
```
```text:出力
A command after failure in a function.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/pKo4g3p6Aj3ZGvk5

#### 例2
```bash
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'."
```
```text:出力
A command after failure in a function.
The right command.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/fTCqXtFFkJMsWfoc

#### 例3
```bash
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'."
```
```text:出力
A command after failure in a function.
Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/gdA0KQ2cLTtncQlB

#### 例4
```bash
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'."
```
```text:出力
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](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html)
> ...
> **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 の右辺に出現する場合（以下を参照）を除く．

#### 例
```bash
set -e
echo $(false)
echo "Did not exit immediately by \`set -e'."
```
```text:出力

Did not exit immediately by `set -e'.
```
https://wandbox.org/permlink/gdOYA3AwScpIOTA0

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

#### 例
```bash
set -e
var=$(false)
echo "Did not exit immediately by \`set -e'."
```
```text:出力
```
https://wandbox.org/permlink/FNYuqtK0W3c4MdQl

#### 対応するドキュメントの記述
> [3.7.1 Simple Command Expansion](https://www.gnu.org/software/bash/manual/html_node/Simple-Command-Expansion.html)
> ...
> 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
```bash
set -e
trap 'false; echo "Did not exit immediately by \`set -e'\''."' EXIT
```
```text:出力
```
https://wandbox.org/permlink/eBVyG5VYINAzJ6bF

#### 例2
```bash
set -e
function func ()
{
  trap 'false; echo "Did not exit immediately by \`set -e'\''."' RETURN
}
func
echo 'A command after function return.'
```
```text:出力
```
https://wandbox.org/permlink/sRfMdKrt2WPUMPF5