나는 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:
something
KornShell93은 내가 원하는 것을 정확하게 수행하지만 Bash는 실패하고 스크립트의 변수 이름이 2행에 동일한 경고를 표시합니다 var
.
변수가 모든 함수에 대해 로컬이기를 원하기 var
때문에 이를 사용 typeset
하지만 Bash는 nameref 자체와 동일한 이름을 가진 변수에 대해 nameref를 "역참조"하는 것을 좋아하지 않는 것 같습니다. local -n
or declare -n
가 없으면 충돌이 발생하기 때문에 사용할 수 없으며 ksh
, 그렇게 해도 문제가 해결되지 않습니다.
내가 찾은 유일한 해결책은각 함수에 고유한 변수 이름을 사용하세요., 현지이기 때문에 다소 어리석은 것 같습니다.
Bash 매뉴얼에는 다음이 포함되어 있습니다 typeset
.
typeset
[...]
-n
각 이름에nameref
속성을 부여하여 다른 변수에 대한 이름 참조가 되도록 하십시오. 이 다른 변수는 의 값으로 정의됩니다name
.name
속성 자체를 변경하는 것 외에도 모든 참조 및 할당은-n
이름 값에서 참조하는 변수에 대해 수행됩니다.[...]
함수 내에서 사용될 때 이 옵션이 제공 되지 않는 한 명령을 사용하는 것처럼 각 이름을 로컬 이름으로
declare
만듭니다 . 변수 이름 뒤에 가 오면 해당 변수의 값은 으로 설정됩니다 .typeset
local
-g
=value
value
분명히 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=foo
using은 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를 사용하는 복잡한 프로그램에 문제가 될 수 있습니다.