web-dev-qa-db-ja.com

Bashで「set -e」を使用するときにERRをトラップする方法

私は簡単なスクリプトを持っています:

#!/bin/bash
set -e
trap "echo BOO!" ERR 

function func(){
    ls /root/
}

func

スクリプトが失敗した場合にERRをトラップしたいと思います(ここではb/cのように、/ rootを調べる権限がありません)。ただし、set -eを使用する場合、トラップされません。 set -eなしの場合、ERRがトラップされます。

Bashのマニュアルページによると、set -eの場合:

... ERRのトラップは、設定されている場合、シェルが終了する前に実行されます。 ...

トラップが実行されないのはなぜですか?マニュアルページからは、そうすべきだと思われます。

23

chepner's answer が最適なソリューションです:必要に応じてset -e( :ERRトラップのあるset -o errexit)、set -o errtraceも使用します(set -Eと同じ)

要するに:set -eEの代わりにset -eを使用してください:

#!/bin/bash

set -eE  # same as: `set -o errexit -o errtrace`
trap 'echo BOO!' ERR 

function func(){
  ls /root/
}

# Thanks to -E / -o errtrace, this still triggers the trap, 
# even though the failure occurs *inside the function*.
func 

man bashset -o errtrace/set -Eについて述べています:

設定すると、ERRのトラップはシェル関数、コマンド置換、およびサブシェル環境で実行されるコマンドに継承されます。このような場合、ERRトラップは通常継承されません。

私が信じていることは起こっている:

  • Without-elsコマンドは関数内で失敗し、関数の最後のコマンドであるため、関数はlsのゼロ以外の終了コードを呼び出し元であるトップレベルスクリプトスコープに報告します。 そのスコープでは、ERRトラップが有効であり、呼び出されます(ただし、トラップからexitを明示的に呼び出さない限り、実行は継続することに注意してください)。

  • あり-e(ただし-Eなし):lsコマンドは失敗します関数内、およびset -eが有効であるため、Bash 即座にが終了します- 関数スコープから直接-そして、有効なERRトラップがないためthere(親スコープから継承されていないため)、トラップは呼び出されません。

manページは間違っていませんが、この動作は明確ではないことに同意します。推測する必要があります。

36
mklement0

関数がトラップを継承するには、set -o errtraceを使用する必要があります。

9
chepner

ERREXITに置き換えると、動作します。

trapコマンドの構文は次のとおりです。trap [COMMANDS] [SIGNALS]

詳細については、 http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html をご覧ください。

4
Ritesh