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


9

シェルオプションを変更して復元する方法について、さまざまなスタック交換サイトとUNIXヘルプフォーラムで多くの質問を読みました。ここで見つけた最も包括的な質問は、「set -x」を「元に戻す」方法です。

受信した知恵は、いずれかの結果をオフに保存することであると考えられるset +oshopt -po、その後、evalそれが後に以前の設定を復元します。

ただし、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フォーマット可能ではありません。

サブシェル関数を使用することも選択肢の1つであることは知っていますが、その場合、出力をログに記録したり、複数の変数を返したりするために多くの問題が生じます。

おそらく関連している:https : //stackoverflow.com/questions/29532904/bash-subshel​​l-errexit-semantics(bash 4.4以降には回避策があるようですが、私はむしろポータブルソリューションを持っていると思います)


これは、bash shoptセットオプション(nullglobなど)を復元しません。
アイザック

回答:


6

あなたがやっていることはうまくいくはずです。ただし、bashはerrexitコマンド置換のオプションをオフにするため、このオプションを除くすべてのオプションが保持されます。これはbashとerrexitオプションに固有です。Bashはerrexit、POSIXモードで実行しているときに保持されます。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

3
なお、bash4.4今持っているlocal -(アラカルトash同等ように)zshS」setopt localoptions(またはデフォルトではありませんksh88では何を)
ステファンChazelas

も参照してくださいshopt -s lastpipe; set +o | IFS= read -rd '' OLDOPTS || :(2つのオプションセットは、bashの特異性の1つです)。
ステファンChazelas

シンプル:OLDOPTS="$(set +o); set -$-"
Isaac、

2

上記の古いバージョンをアルパイン3.6で試した後、次の非常に簡単なアプローチを採用しました。

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

... my stuff

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

ドキュメントに従って、「$-」は現在アクティブなオプションのリストを保持します。うまく機能しているようですが、何か不足していますか?


@Isaac $-にはアクティブなオプションのリストしか含まれていないため、「set + euf」を使用ます。つまり、リセット時に最初にすべてを非アクティブ化してから、以前にアクティブだったオプションのリストのみを再アクティブ化します(これらは通常変更する唯一のオプションであるため、「euf」を選択します。一般的な用途では、可能なオプションの完全なリストが含まれているはずです)。'set + vx'についての良い点は、上の例をそれに応じて変更しました
Erich Eichinger

これはerrexitでは機能しますが、たとえば境界チェックやグロビングでは機能しません(set -uf
Erich Eichinger

@Isaac修正しました。何を間違えたかわかりません。あなたの助けをありがとう!また、-cフラグの問題が発生して解決したことを確認してください。以前にコメントを見ていただければ幸いです;)
Erich Eichinger

@Isaacのフィードバックに基づいて上記のソリューションを更新しました
Erich Eichinger

1

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)

1

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

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

できました。


参照してください[[ -o errexit ]](kshが/ bashの/ zshの)と[ -o errexit ](bashは/ kshの/ヤシュ)
ステファンChazelas

これshoptはbashに有効なコマンドです。これを回避する意味のある理由はありません(私が回答をどのように記述するかについての個人的な好みの問題です)。意味のある変更ではありません。@StéphaneChazelas–
Isaac
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.