ローカライズされた店舗オプション

ローカライズされた店舗オプション

shopt関数のオプション値を一時的に変更したいと思います。

組み込み関数を使用して設定できるオプションは、関数本体内で設定setできます。local -以下はサンプルコードです

#!/bin/bash

# Enable the options globally 
set -u
[[ "$SHELLOPTS" =~ nounset ]] && echo "1: nounset enabled"
shopt -s "extglob"
[[ "$BASHOPTS" =~ extglob ]] && echo "1: extglob enabled"

fun () {
    #Enable the options locally just in fun
    local -
#    local BASHOPTS
    set +u
    [[ "$SHELLOPTS" =~ nounset ]] || echo "2: nounset disabled"
    shopt -u "extglob"
    [[ "$BASHOPTS" =~ extglob ]] || echo "2: extglob disabled"
}

fun

if [[ "$SHELLOPTS" =~ nounset ]]; then
    echo "3: nounset enabled"
else
    echo "3: nounset disabled"
fi

if [[ "$BASHOPTS" =~ extglob ]]; then
    echo "3: extglob enabled"
else
    echo "3: extglob disabled"
fi

出力を含む

1: nounset enabled
1: extglob enabled
2: nounset disabled
2: extglob disabled
3: nounset enabled
3: extglob disabled

ご覧のとおり、オプションはnounset関数本体に追加するだけでローカライズできます。local -私は同じshoptオプションが欲しいです。現在の回避策は、まずオプションが有効になっていることを確認することです。

if ! shopt -q extglob; then
    extglobchanged=1
    shopt -s extglob
fi

その後、最終的に以前の状態に戻ります。

[[ "$extglobchanged" == 1 ]] && shopt -u extglob

中間コードが失敗する可能性があるすべての状況を処理する必要があるため、これは良い解決策ではありません。 (関数が終了する前に上記の行が実行されていることを確認する必要があります。)

質問: オプションをローカライズできますかshopt

--編集 1--

やる気

私のbashツールキットには、ユーザーにYesまたはNoを問い合わせる機能があります。私はcaseパターンをorに関連付けるためにこの目的のために拡張globを使用します。これは私のコードです

 yesnoquery () {
    local extglobchanged=0
    local returnvalue=2
    while true; do
    if read -p "$1" answer; then
        if ! shopt -q extglob; then
        extglobchanged=1
        shopt -s extglob #enable for pattern matching
        fi
        eval '
        case "$answer" in
            @([Yy]|[Yy][Ee][Ss])) returnvalue=0;; #true
            @([Nn]|[Nn][Oo])) returnvalue=1;; #false
            * ) echo "Please answer \"y\" or \"n\"";;   
        esac
        '
        [[ "$extglobchanged" == 1 ]] && shopt -u extglob #revert changes
        [[ "$returnvalue" != 2 ]] && return "$returnvalue"
    else
        return 2 #reading returned error
    fi
    done
}

こんな些細なことをするには長い時間がかかるようです。shoptオプションextglobをローカライズできる場合は、caselikeから直接返すことができます@([Yy]|[Yy][Ee][Ss])) return 0;;。また、この[[ "$extglobchanged" == 1 ]]...行とその後の行は必要ありません。

主な問題

現在、関数が終了する可能性があるすべての場所に変更を元に戻すには、手動でコードを追加する必要がありますshopt

私の考えは、これらの変更をローカライズする必要があるということですlocal -。ローカライズのない他のソリューションは完璧です。

これが単に言語制限であれば、これは私の質問に対する答えでもあります。

答え1

はい、可能です:

fun()
{
    local -
    # store state of all options.
    oldstate="$(shopt -p)"
    :
    :

    set +vx; eval "$oldstate"
}

特別な状況がありますerrexit

答え2

私のニーズに合った解決策を見つけたようです。サブシェルで関数本体を実行する。サブシェルで関数本体を実行することの長所と短所、および一般的なユースケースもここで数回議論されています。[1][2][サム]

で述べたように[4]Bashmanページから取得した関数の一般的な構文は次のとおりです。

[ function ] name () compound-command [redirection]

Aは、Compound comamand括弧構文を使用して開始された現在のシェルの子プロセスです(list)man bash次のように定義します(ハイライトを追加):

(list)listサブシェル環境で実行します(以下のコマンド実行環境を参照)。変数の割り当てと組み込みコマンドシェルに影響を与える環境完了後、コマンドは無効になります。戻り状態はリストの終了状態です。

私のユースケースには2つのことが必要です。

  • shoptオプション変更のローカライゼーション効果extglob
  • 返品状態を維持

サブシェルによるshoptオプションの変更は、extglob周囲のシェルに漏れません。また、関数の任意の時点で返すことができます。もちろん、欠点は、サブシェルの起動に追加のリソースが必要であることです。

デモ

今より短い関数は次のようになります。

yesnoquery () (
    while true; do
        if read -p "$1" answer; then
            shopt -s extglob
            eval '
            case "$answer" in
                @([Yy]|[Yy][Ee][Ss]) ) return 0;; #true
                @([Nn]|[Nn][Oo]) ) return 1;; #false
                * ) echo "Please answer \"yes\" or \"no\"";;   
            esac
            '
        else
            return 2 #reading returned error
        fi
    done
)

Caseステートメントでは、一時変数に保存する代わりに直接返すことができます。ローカライズされた変数は必要なくなり、両方の条件が保存されました。

shoptこれらのオプションが実際にローカルであることは、問題のコードサンプルコードですばやく確認できます。

#!/bin/bash

# Enable the options globally 
set -u
[[ "$SHELLOPTS" =~ nounset ]] && echo "1: nounset enabled"
shopt -s "extglob"
[[ "$BASHOPTS" =~ extglob ]] && echo "1: extglob enabled"

fun () {
    set +u
    [[ "$SHELLOPTS" =~ nounset ]] || echo "2: nounset disabled"
    shopt -u "extglob"
    [[ "$BASHOPTS" =~ extglob ]] || echo "2: extglob disabled"
}

fun

if [[ "$SHELLOPTS" =~ nounset ]]; then
    echo "3: nounset enabled"
else
    echo "3: nounset disabled"
fi

if [[ "$BASHOPTS" =~ extglob ]]; then
    echo "3: extglob enabled"
else
    echo "3: extglob disabled"
fi

変更されたのは、すべてここにある括弧({および削除された重複ローカル変数)です。出力

1: nounset enabled
1: extglob enabled
2: nounset disabled
2: extglob disabled
3: nounset enabled
3: extglob enabled

効果が正常にターゲティングされたことを示します。

一般化する

関数本体としてのサブシェル実行環境は、bashシェルオプションの効果をローカライズするのに役立つことが証明されています。この変数を変更しても関数範囲外のコードには影響しないため、case関数本文内の文はこれから利点を得ることができます。extglobリソースが不足すると、サブシェルの起動に伴う追加のオーバーヘッドがパフォーマンスを不必要に遅くする可能性があります。

関連情報