errexitを含むすべてのシェルオプションを保存および復元する方法

errexitを含むすべてのシェルオプションを保存および復元する方法

シェルオプションを修正して復元する方法に関するさまざまなスタック交換サイトとUnixヘルプフォーラムで多くの質問を読んだ。ここで見つけた最も包括的な質問は次のとおりです。`set -x`を「元に戻す」方法は?

これ知恵を得る以前の設定を保存して後で復元した結果set +oのようです。shopt -poeval

ただし、bash 3.xおよび4.xを使用したセルフテストでは、errexitコマンド置換を実行したときにオプションが正しく保存されませんでした。

以下は、問題を示すサンプルスクリプトです。

set -o errexit
set -o nounset
echo "LOCAL SETTINGS:"
set +o
OLDOPTS=$(set +o)
echo
echo "SAVED SETTINGS:"
echo "$OLDOPTS"

と出力(無関係な変数をいくつか削除しました):

LOCAL SETTINGS:
set -o errexit
set -o nounset

SAVED SETTINGS:
set +o errexit
set -o nounset

これは非常に危険に見えます。私が書くほとんどのスクリプトは、errexitコマンドが失敗した場合に実行を停止することに依存しています。私は私のスクリプトの1つでこれによって発生したバグを見つけました。最終的に復元しなければならなかった関数がerrexitそれを上書きし、再びデフォルトに設定されました。去るスクリプトの進行中。

私がやりたいことは、必要に応じてオプションを設定してから復元できる関数を書くことです。みんな終了する前にオプションを正しく選択してください。しかし、コマンド置換によって呼び出されるサブシェルではerrexit継承できないようです。

set +oコマンド置換を使用したり、FIFOリングをジャンプしたりせずに結果を保存する方法がわかりません。読み取ることはできますが、書き込めない、または$SHELLOPTS使用できない形式ですeval

別の方法は、次のものを使用することです。サブシェル機能しかし、これは出力を記録し、複数の変数を返すのに多くの問題を引き起こします。

おそらく関連があるでしょう:https://stackoverflow.com/questions/29532904/bash-subshel​​l-errexit-semantics(bash 4.4以降の解決策があるようですが、ポータブルソリューションを好む)

答え1

あなたがやっていることはうまくいくでしょう。ただし、 bash はコマンド置換でそのオプションをオフにするので、errexitそのオプションを除くすべてを保持します。これはbashによって異なり、errexitオプションによって異なります。 Bash はerrexitPOSIX モードで実行しても保持されます。 bash 4.4以降、bashはerrexitコマンド置換で有効かどうかを消去しませんshopt -s inherit_errexit

このオプションは、コマンド置換内でコードが実行される前にオフになるため、外部で確認する必要があります。

OLDOPTS=$(set +o)
case $- in
  *e*) OLDOPTS="$OLDOPTS; set -e";;
  *) OLDOPTS="$OLDOPTS; set +e";;
esac

これらの複雑さが気に入らない場合は、代わりにzshを使用してください。

setopt local_options

答え2

Alpine 3.6で上記の以前の方法を試した後、次の簡単な方法を採用しました。

OLDOPTS="$(set +o); set -${-//c}"
set -euf -o pipefail

... my stuff

# restore original options
set +vx; eval "${OLDOPTS}"

ドキュメントによると、「$-」には現在アクティブなオプションのリストがあります。うまくいくようですが、私が何かを見逃しているのでしょうか?

答え3

errexitプロセス交換に伝播されます。

set -e

# Backup restore commands into an array
declare -a OPTS
readarray -t OPTS < <(shopt -po)

set +e

# Restore options
declare cmd
for cmd in "${OPTS[@]}"; do
    eval "$cmd"
done

確認する:

$  shopt -po errexit
set -o errexit

ヒットバージョン:

$ bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

答え4

簡単な解決策は、errexit設定を次に追加することですOLDOPTS

OLDOPTS="$(set +o)"
[ "${BASH_VERSION:+x}" ] && shopt -qo errexit && OLDOPTS+=";set -e" || true

完璧。

関連情報