bash 쉘 함수에는 순환 이름 참조가 있지만 ksh에는 없습니다.

bash 쉘 함수에는 순환 이름 참조가 있지만 ksh에는 없습니다.

나는 Bash와 KornShell93에서 작동하기를 바라는 일련의 쉘 함수를 작성하고 있지만 Bash를 사용할 때 "순환 이름 참조" 경고가 나타납니다.

문제의 본질은 이렇습니다.

function set_it {
    typeset -n var="$1"

    var="hello:$var"
}

function call_it {
    typeset -n var="$1"

    set_it var
}

something="boff"
call_it something
echo "$something"

실행하세요:

$ ksh script.sh
hello:boff

$ bash script.sh
script.sh: line 4: warning: var: circular name reference
hello:

somethingKornShell93은 내가 원하는 것을 정확하게 수행하지만 Bash는 실패하고 스크립트의 변수 이름이 2행에 동일한 경고를 표시합니다 var.

변수가 모든 함수에 대해 로컬이기를 원하기 var때문에 이를 사용 typeset하지만 Bash는 nameref 자체와 동일한 이름을 가진 변수에 대해 nameref를 "역참조"하는 것을 좋아하지 않는 것 같습니다. local -nor declare -n가 없으면 충돌이 발생하기 때문에 사용할 수 없으며 ksh, 그렇게 해도 문제가 해결되지 않습니다.

내가 찾은 유일한 해결책은각 함수에 고유한 변수 이름을 사용하세요., 현지이기 때문에 다소 어리석은 것 같습니다.

Bash 매뉴얼에는 다음이 포함되어 있습니다 typeset.

typeset[...]

-n 각 이름에 nameref속성을 부여하여 다른 변수에 대한 이름 참조가 되도록 하십시오. 이 다른 변수는 의 값으로 정의됩니다 name. name속성 자체를 변경하는 것 외에도 모든 참조 및 할당은 -n 이름 값에서 참조하는 변수에 대해 수행됩니다.

[...]

함수 내에서 사용될 때 이 옵션이 제공 되지 않는 한 명령을 사용하는 것처럼 각 이름을 로컬 이름으로 declare만듭니다 . 변수 이름 뒤에 가 오면 해당 변수의 값은 으로 설정됩니다 .typesetlocal-g=valuevalue

분명히 Bash의 이름 참조 및 함수 로컬 변수에 몇 가지 문제가 있습니다.

따라서 질문은 이 경우에 Bash의 이름 참조 변수 처리가 누락된 것입니까, 아니면 Bash의 버그/버그 기능입니까?

고쳐 쓰다:저는 현재 GNU bash, version 4.3.39(1)-release (x86_64-apple-darwin15)및 과(와) 함께 일하고 있습니다 GNU bash, version 4.3.46(1)-release (x86_64-unknown-openbsd6.0). macOS와 함께 제공되는 Bash는 너무 오래되어 이름 참조에 대해 알지 못합니다.

고쳐 쓰다: 더 짧게:

function bug {
    typeset -n var="$1"
    printf "%s\n" "$var"
}

var="hello"
bug var

밝혀지다 bash: warning: var: circular name reference. var기능상범위가 달라야 합니다var글로벌 관점 에서 . 이는 호출자에게 불필요한 제한을 부과합니다. 제한 사항은 "이 함수의 (로컬) 이름 참조와 이름 충돌이 발생할 수 있으므로 임의로 변수 이름을 지정할 수 없습니다"입니다.

답변1

Chet Ramey (Bash 관리자)설명하다

올해 초 bug-bash에서 nameref에 대한 광범위한 논의가 있었습니다. 이 동작을 변경하는 방법에 대한 건전한 제안이 있습니다. bash-4.4가 출시된 후에 이를 살펴보겠습니다.

그동안 나는 로컬 nameref 변수의 이름을 약간 난독화하여 라이브러리 내에서 그리고 (희망적으로) 전역 쉘 변수 이름과 충돌하지 않도록 했습니다.


5.0 에서는 bash이 문제가 약간 해결되었습니다(그러나 실제로 수정되지는 않았습니다). 관찰된 동작은 다음과 같습니다.

$ foo () { typeset -n var="$1"; echo "$var"; }
$ var=hello
$ foo var
bash: typeset: warning: var: circular name reference
bash: warning: var: circular name reference
bash: warning: var: circular name reference
hello

이는 작동함을 보여 주지만 몇 가지 주의 사항이 있습니다.

관련된정보항목은 말한다

i. A nameref name resolution loop in a function now resolves to a variable by
that name in the global scope.

답변2

Bash의 이름 참조는 약간 어리석고 위에서 말한 것과 거의 정확히 일치합니다.이름. 대부분의 범용 프로그래밍 언어에서 포인터 및 참조와 같이 참조 변수의 "ID"를 캡처하지 않습니다.

declare -n ref=var따라서 using을 실행하면 ref변수 이름을 찾아 var해당 값을 사용합니다.

다음을 실행하면 declare -n foo=foousing은 foo변수에 대한 이름 조회를 수행 foo하고... 음, 루프임을 인식하고 경고를 발행합니다.

또한 참조로 찾은 변수는 nameref 설정 시 해당 이름으로 존재했던 변수와 다를 수 있습니다. 예를 들어 다음 스크립트를 고려해보세요.

$ cat nameref.sh 
#!/bin/bash

var="from main"
declare -n ref=var               # a

echo "main:  \$ref='$ref'"

foo() {
    local var="from foo"
    bar
}

bar() {
    echo "bar(): \$ref='$ref'"   # b
}

foo

"a" 줄에서는 최상위 수준이 var표시되지만(참조를 저장한다고 생각할 수도 있음) $ref"b" 줄에서 사용하면 표시되는 내용은 참조 var에서 foo()찾은 것입니다.

$ bash nameref3.sh 
main:  $ref='from main'
bar(): $ref='from foo'

함수에서 동일한 결과를 얻을 수도 있습니다.

$ cat nameref4.sh

var=outer
foo() {
    declare -n ref=var
    echo "ref=$ref"
    local var=inner
    echo "ref=$ref"
}
        
foo

첫 번째는 echo외부 컨텍스트에서 값을 가져오고, 두 번째는 에서 방금 할당한 값을 가져옵니다 foo().

Kusalananda의 답변에서 언급했듯이 Bash 5.0은 루프가 발생할 때 지정된 변수의 최상위 인스턴스를 가져옵니다. 그러나 이는 여전히 경고를 발생시키고 여러 개의 중첩된 함수가 있는 경우 호출 스택 중간에서 변수를 참조할 수 없기 때문에 별로 도움이 되지 않습니다.


이 점에서 Ksh는 다릅니다. 나는 그것이 정확히 무엇인지는 모르지만 typeset -n ref=var즉각적인 외부 컨텍스트에서 변수를 구문 분석하고 실제 ID를 저장하는 것 같습니다 . 이번에도 지역변수일반적으로 말하면Bash와 달리 Ksh에서는 혼합에 이름 참조가 없더라도 Ksh를 사용하는 복잡한 프로그램에 문제가 될 수 있습니다.

관련 정보