현지화된 매장 옵션

현지화된 매장 옵션

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 툴킷에는 사용자에게 예 또는 아니오를 쿼리하는 기능이 있습니다. 나는 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]Bash man페이지에서 가져온 함수의 일반 구문은 다음과 같습니다.

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

A는 Compound comamand괄호 구문을 사용하여 시작된 현재 쉘의 하위 프로세스일 수 있습니다 (list). man bash다음과 같이 정의하십시오(강조 추가):

(list) list 서브셸 환경에서 실행합니다(아래 명령 실행 환경 참조). 변수 할당 및 내장 명령쉘에 영향을 미치는 환경완료 후에는 명령이 더 이상 유효하지 않습니다. 반환 상태는 목록의 종료 상태입니다.

내 사용 사례에는 두 가지가 필요합니다.

  • 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리소스가 부족한 경우 하위 셸을 시작하는 데 따른 추가 오버헤드로 인해 성능이 불필요하게 느려질 수 있습니다.

관련 정보