RETURNトラップを使用する

RETURNトラップを使用する

関数があり、その中でオプションを使用したいと考えています。しかし、スクリプトの別の部分では が設定されていない可能性があるので、pipefail単純に にしたくありません。もちろん、後で を行うこともできますが、関数の外部で が設定された場合、予期しない動作が発生する可能性があります。set -o pipefailpipefailset +o pipefailpipefail

もちろん、が設定されているfalse | true場合は、 の終了コードを基準として使用することもできますpipefailが、これは少し不潔なようです。

設定された bash オプションを確認するための、より一般的な (そしておそらく標準的な) 方法はありますか?

答え1

上記でbash-4.4は、以下を使用できます。

myfunction() {
  local -
  set -o someoption
  ...
}

いくつかの注意事項:

  • は からコピーされますash。 では、 (オプションのセットが格納されている) をローカルにashするという考え方です。 では、は変数の値や属性に影響を与えないため(最初に設定を解除する場合とは反対に)、 で機能します。 それでも、 の場合と同じように を特別なケースとして機能させます。つまり、 の設定を解除したり、デフォルトの動作に戻したりすることはありません。$-ashashlocalbashbashash$-
  • これは で設定されたオプションのみをカバーしset -o option、 で設定されたオプションはカバーしませんshopt -s option(bashは 2 セットのオプションを持つ唯一のシェルです)。
  • ローカル変数の場合と同様に、静的ではなく動的スコープです。 の値は$-関数から戻ると復元されますが、新しい設定は関数によって呼び出される他の関数またはソース スクリプトに影響します。静的スコープが必要な場合は、ksh93 に切り替えて以下を使用する必要があります。

    function myfunction {
      set -o myoption
      ...
      other_function
    }
    

    構文 (Bourne 構文ではない)other_functionを使用して宣言されている限り、 は影響を受けません。function other_function {other_function()...

  • オプションをリセットしないので、関数は他のコードによって設定されたオプションの影響を受ける可能性があります。これを避けるには、zsh代わりに を使用します。zshの同等のものはlocal -ですset -o localoptionsが、よく知られている を取得することもできます。エミュレーションモード地元で。 例えば:

    myfunction() {
      emulate -L zsh
      set -o someoption
      ...
    }
    

    は、zsh のデフォルトのオプション セット (いくつかの例外があります。ドキュメントを参照してください) から開始し、その上にオプションを追加します。これは ash/bash のような動的スコープでもありますが、次のように他の関数を呼び出すこともできます。

    emulate zsh -c 'other_function...'
    

    これをother_function通常の zsh オプション セットで呼び出すためです。

    zshには、オプションをグローバルに変更することを避けるための演算子もいくつかあります(/の(N)/ (D)glob修飾子、大文字と小文字を区別しないグロブのための glob 演算子、との混在を避ける、など) 。nullglobdotglob(#i)${(s:x:)var}$IFS${~var}リクエストパラメータ展開時のグロブ(noglob他のBourne系シェルでは避ける(!) それ)...

答え2

変数$SHELLOPTSには、設定されたすべてのオプションがコロンで区切られて保持されます。例として、次のようになります。

braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor

特定のオプションを確認するには、次の操作を実行します。

# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
    set -o pipefail;
    PIPEFAIL=0;
else
    PIPEFAIL=1;
fi

# do something here

# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
    set +o pipefail;
fi

答え3

関数内でシェル オプションを設定するが、呼び出し元に影響を与えたくない場合は、次の 2 つの方法があります。

オプション1: サブシェル

関数をサブシェル内に配置します (関数のステートメントを中括弧ではなく括弧で囲むことに注意してください)。

function foo() (
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
)

次に、呼び出し元は pipefail を設定せずに、影響を受けません。

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

または

オプション2: テストとリセット

必要に応じてオプションをテストしてリセットできます (関数ステートメントを中括弧で囲んでいることに注意してください。サブシェルはありません)。

function foo() {
  shopt -q -o pipefail
  local resetpipefail=$?
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
  # only reset pipefail if needed
  [[ $resetpipefail -eq 1 ]] && set +o pipefail  
}

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

感謝しますクオングルム彼らのshopt -q答え

答え4

RETURNトラップを使用する

トラップで指定されたコマンドは、またはRETURNで実行されたシェル関数またはシェル スクリプトが戻った後に実行が再開される前に実行されます。.source

[...]
オプション-p[to shopt] を使用すると、出力は入力として再利用できる形式で表示されます。

https://www.gnu.org/software/bash/manual/bash.html

foo() {
  trap "$(shopt -p -o pipefail)" RETURN
  set -o pipefail
  echo inside foo
  shopt -o pipefail
  # ...
}

テスト

$ shopt -o pipefail
pipefail        off
$ foo
inside foo
pipefail        on
$ shopt -o pipefail
pipefail        off

次の関数テンプレートは、関数が返されるときに、setおよびオプションの両方が元の値に復元されることを保証する一般的なケースを処理します。shopt

foo() {
  trap "$(shopt -p -o; shopt -p)" RETURN # ①
  # ...
}

①はshopt -p -o標準的な同等物に簡略化できますset +o

関連情報