LoginSignup
4
1

More than 3 years have passed since last update.

set -eしている状態でサブシェルの返り値をlocalで受けると、サブシェルが0以外の終了ステータスを返してもexitしない

Posted at

環境

$ 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以外になったときに終了するようになる。

例えば以下のようなスクリプトがあったとき、

test.sh
#!/bin/bash

echo start.
false
echo done.

set -eしていない状態でこれを実行すると、

$ ./test.sh
start.
done.
$ echo $?
0

falseは終了ステータス1を返すが、スクリプトは最後まで実行されている。

ここで set -eを加えると、

test.sh
#!/bin/bash
set -e

echo start.
false
echo done.
$ ./test.sh
start.
$ echo $?
1

のように、終了ステータスが0以外を返したときにスクリプトの実行が終了する。

サブシェルの返り値をlocalで受ける

サブシェルを使って子プロセスでコマンドを実行し、その標準出力を変数に格納するケースを考えてみる。以下のスクリプトでは関数内でサブシェルの実行をおこなっている(falseコマンドは何も返さないので意味のない例だが)。

test.sh
#!/bin/bash
set -e

function func() {
  ret=$(false)
}

echo start.
func
echo done.

これを実行すると、

$ ./test.sh
start.
$ echo $?
1

となり、サブシェルが終了ステータスで1を返す場合もやはりスクリプトの実行は終了することがわかる。

bashでは変数はグローバルスコープに置かれてしまうので、local retとしてretのスコープを関数に限定してみる。

test.sh
#!/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. サブシェルが実行される(終了ステータス1を返す)
  2. local retが実行される(終了ステータス0を返す)

なので、 local ret=$(false)全体としては0になり、そのまま最後まで実行されてしまう。

これを防ぐためには、 local retの宣言をあらかじめおこなっておく。

test.sh
#!/bin/bash
set -e

function func() {
  local ret
  ret=$(false)
}

echo start.
func
echo done.
$ ./test.sh
start.
$ echo $?
1

サブシェルの実行が1を返すため、スクリプトの実行も終了している。

参考

4
1
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
4
1