set -eおよび/またはトラップがアクティブであるにもかかわらず、bashの終了ステータスがキャッチされない


8

誰かが以下のコードスニペットでbash / set -eの動作を説明できますか?

#!/bin/bash

# Comment if you want to test the trap only
set -e -o pipefail -u -E

# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR

function reproduce() {
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
}

reproduce

# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.

status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
    echo "FIXME: why was status code not caught by set -e ?"
    echo "Executing false to prove set -e is still active"
    false
    # If the following is not executed then it proves -e is still active
    echo "set -e not active !!!!!"
    exit 2
fi

これを実行すると、次のようになります。

$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT

終了コードの確認

$ echo $?
1

バッシュバージョン

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

と同様に再現

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

コメントに関する追加のメモ(とにかくすべての提案に感謝します):

  • コメントトラップは、観察された奇妙な動作を変更しません
  • トラップのみを保持するためにset -eを削除すると、トラップもトリガーされます

1
私は組み合わせないだろうset -etraptrapエラー時に呼び出され、「echo ERROR CAUGHT」が呼び出されます。trapよりも優先順位が高い印象ですset -e。またset -e、推奨されないBash FAQによると 、mywiki.wooledge.org/BashFAQ/105を確認してください
stephanmg

コメントトラップは何も変更しません
raphael.glon

trap代わりにメカニズムを使用するだけtrap "exit 2" ERRです。また、私にとって、スクリプトは「ステータス0」のみを出力します。ERRトラップはシェル関数に継承されていないようですが、これは役立ちますset -o errtraceか?それ以外の場合は、set -e最初に避けなければならない理由について上記のリンクを参照してください。
stephanmg

回答:


3

簡単にしましょう。あなたが扱っている問題を再現するために必要なコードの最小量は

set -e
: $((+)) # arithmetic expansion error
echo survived

標準によると、これは印刷することはありませんsurvivedそれはPOSIXシェルが非対話的に実行すると、すぐに拡張エラー時に終了するものと述べています。しかし、一見バッシュはそうは思わない。この違いはmanページに明示的に記載されていませんが、POSIXモードの説明では、

  1. 算術展開の構文エラーの結果、無効な式が発生した場合、非インタラクティブシェルは終了します。

これは、デフォルトの動作モードでは、非対話型のBashセッションがそのようなエラーで終了しないことを意味しますが、ご存知のように、errexitメカニズムやERRトラップもトリガーしません。代わりに、ゼロ以外の値を先へ$?の移動に割り当てます。

これを克服して期待される動作を得るにはreproduce、次のように定義する必要があります

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)

このようにして、拡張エラーはサブシェルで発生し、ゼロ以外のステータスで終了するため、errexitとtrapはそれをキャッチできます。


dash-oのリクエストに応じて、a式が有効な場合に現在の実行環境に設定するバージョンを以下に示します

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}

2

表面的には、bashがさまざまな構文エラーでトラップをトリガーしないように見えます。コマンド(外部、組み込み)が実行された(そしてゼロ以外の値が返された)場合にのみ、ERRトラップがトリガーされます。

manページから:

sigspecがERRの場合、次の条件に従って、パイプライン(単一の単純なコマンドで構成される場合があります)、リスト、または複合コマンドがゼロ以外の終了ステータスを返すたびに、コマンドargが実行されます...

ERRトラップはPIPELINEにのみ適用されます。bashが構文エラーを識別した場合、パイプラインを実行する前に中止されるため、トラップは発生しません。彼の '-e'のドキュメントには同じ条件(if a pipeline ... exit with non-zero status)が指定されていますが、実際の動作は異なります。

パイプライン実行があるため、他の拡張を試行する場合-たとえば、コマンド拡張-トラップがトリガーされます。

  • a = $(bad-commands)
  • a = $([)

算術展開でさまざまな構文エラーを使用すると、トラップはトリガーされません-パイプラインがありませんでした。

  • a = $((2+))
  • a = $((2 @))

また、他のbash構文エラーはトラップをトリガーしません:()[[ ]]

ソーススクリプトを大幅に変更する必要のないソリューションを見つけることができませんでした。bashチームにバグ/機能のリクエストを提出できますか?


2
面白い事実:サブシェルで実行する( a=$((1109962735 - hello=12272 + 1)) )( reproduce )、トラップを実行します。
KamilCuk

残念ながら、式が有効な場合aは設定されません。
dash-o
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.