環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.6
BuildVersion: 18G84
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
前提
bashスクリプトでset -e
すると、それ以降のコマンド実行で終了ステータスが0以外になったときに終了するようになる。
例えば以下のようなスクリプトがあったとき、
#!/bin/bash
echo start.
false
echo done.
set -e
していない状態でこれを実行すると、
$ ./test.sh
start.
done.
$ echo $?
0
false
は終了ステータス1を返すが、スクリプトは最後まで実行されている。
ここで set -e
を加えると、
#!/bin/bash
set -e
echo start.
false
echo done.
$ ./test.sh
start.
$ echo $?
1
のように、終了ステータスが0以外を返したときにスクリプトの実行が終了する。
サブシェルの返り値をlocalで受ける
サブシェルを使って子プロセスでコマンドを実行し、その標準出力を変数に格納するケースを考えてみる。以下のスクリプトでは関数内でサブシェルの実行をおこなっている(false
コマンドは何も返さないので意味のない例だが)。
#!/bin/bash
set -e
function func() {
ret=$(false)
}
echo start.
func
echo done.
これを実行すると、
$ ./test.sh
start.
$ echo $?
1
となり、サブシェルが終了ステータスで1を返す場合もやはりスクリプトの実行は終了することがわかる。
bashでは変数はグローバルスコープに置かれてしまうので、local ret
としてret
のスコープを関数に限定してみる。
#!/bin/bash
set -e
function func() {
local ret=$(false)
}
echo start.
func
echo done.
これを実行すると、
$ ./test.sh
start.
done.
$ echo $?
0
set -e
が指定されていてかつサブシェルが終了ステータス1を返すにも関わらず、スクリプトは最後まで実行されてしまっている。
これはなぜか?
原因と対策
原因は local ret
が終了ステータス0を返すからである。
順序としては、
- サブシェルが実行される(終了ステータス1を返す)
-
local ret
が実行される(終了ステータス0を返す)
なので、 local ret=$(false)
全体としては0になり、そのまま最後まで実行されてしまう。
これを防ぐためには、 local ret
の宣言をあらかじめおこなっておく。
#!/bin/bash
set -e
function func() {
local ret
ret=$(false)
}
echo start.
func
echo done.
$ ./test.sh
start.
$ echo $?
1
サブシェルの実行が1を返すため、スクリプトの実行も終了している。