한 줄에 일부 컨텍스트를 저장하기 위해 약간의 실험을 수행했습니다.
내가 원하는 대로 정확하게 작동합니다. 따라서 이 게시물의 목적은 다음과 같습니다. 1. 커뮤니티와 공유합니다. 2. 추악하고 신비하기 때문에 개선하거나 완전히 다른 해결책을 찾으십시오.
상황은 다음과 같습니다. 전역 변수가 있는데 이를 함수에서 로컬로 변경하고 마지막에 복원할 수 있기를 원합니다.
x()
{
typeset loc=$glob
glob=<val>
(...)
glob=$loc
}
이것은 나에게 너무 장황하고 오타가 필요합니다.
그래서 나는 이것을 시도했습니다 (사용 가능한 코드):
#!/bin/bash
glob=1
indent=0
# appliance
trace()
{
printf "%$((${indent}*3))s %s\n" "" "$1"
}
# the context stuff (creation and destruction)
# in variable, because with a function, I'd need to create a $(sub shell) and it wouldn't work
new_glob='trap restore_context RETURN;typeset loc=$glob;glob'
restore_context()
{
res=$?
glob=$loc
trap - RETURN
}
# common test stuff, to isolate the traces
test_call()
{
typeset res
trace "in ${FUNCNAME[1]}, before $1,glob=$glob"
(( indent++ ))
eval $1
res=$?
(( indent-- ))
trace "in ${FUNCNAME[1]}, res of $1=$res"
trace "in ${FUNCNAME[1]}, after $1,glob=$glob"
return $res
}
# Russian dolls function
f()
{
eval "$new_glob=6"
test_call g
return 16
}
g()
{
eval "$new_glob=7"
test_call h
return 17
}
h()
{
eval "$new_glob=8"
trace "in h, glob=$glob"
i
return 18
}
i()
{
trace "in i, glob=$glob"
}
# main
test_call f
이 스크립트는 f를 호출하고, f는 g를 호출하고, g는 h를 호출합니다. 각 함수는 전역 변수를 변경한 다음 복원합니다.
산출:
# ./test_rtrap
in main, before f,glob=1
in f, before g,glob=6
in g, before h,glob=7
in h, glob=8
in i, glob=8
in g, res of h=18
in g, after h,glob=7
in f, res of g=17
in f, after g,glob=6
in main, res of f=16
in main, after f,glob=1
여기서 중요한 것은 이 기능이나()지역 변수를 사용하는 것처럼 1이 아닌 8을 인쇄합니다.
이 결과를 얻기 위해 RETURN에 트랩 기능을 사용했습니다.
이제 위의 함수 x는 다음과 같습니다.
x()
{
eval "$new_glob=6"
(...)
}
불행하게도 코드의 일부를 포함하는 평가판과 변수를 사용해야 합니다. 부자연스럽고 상당히 신비스럽습니다. 하지만 거기에서 함수를 사용하려면 서브셸이 필요하고 관련 변수 컨텍스트 문제가 있기 때문에 필요합니다.
따라서 완벽하지는 않지만 예쁘지도 않고 장황하지도 않지만 작동합니다.
추악한 평가 "$new_glob=6"보다 더 좋은 방법이 있습니까?
답변1
글쎄요! 적어도 표시된 예에서는 그렇습니다.
new_glob 변수 eval "$new_glob=6"
로 바꾸고 삭제합니다.local glob=6
즉, 글을 쓰는 것보다
x()
{
typeset loc=$glob
glob=<val>
(...)
glob=$loc
}
쓰다
x()
{
typeset glob
glob=<val>
}
선택적 으로 또는 typeset
로 바꾸십시오 .local
declare
배쉬는다이내믹 레인지언어. 현재 구현 전략은 링크된 기사에 설명되어 있습니다.
더 간단한 구현은 간단한 전역 변수를 사용하여 동적 변수를 나타내는 것입니다. 로컬 바인딩은 프로그램에 보이지 않는 스택의 익명 위치에 원래 값을 저장하여 수행됩니다. 바인딩 범위가 종료되면 원래 값이 이 위치에서 복원됩니다. 실제로 동적 범위 지정은 이런 방식으로 시작되었습니다. Lisp의 초기 구현에서는 지역 변수를 구현하기 위해 이 확실한 전략을 사용했으며 GNU Emacs Lisp와 같이 여전히 사용 중인 일부 방언에서는 이러한 관행이 유지되었습니다.
제공된 bash 코드를 거의 설명합니다.
답변2
좋아요(Icarus에게 감사드립니다). 여기서 IMHO bash는 정말 이상합니다. 나는 기본적으로 그 동작을 다시 구현했습니다.
Bash의 지역 변수는 지역 변수가 아닙니다! 전역/환경 변수의 값을 로컬에서 변경/설정하고 "로컬 정의" 기능을 종료할 때 해당 값을 복원합니다. 이를 동적 범위 지정이라고 합니다.
bash의 로컬은 "전역 변수의 로컬 값"을 의미하는데, 이는 나에게 이상합니다. 예를 들어 ksh93은 다르게 동작하며 지역 변수는 실제로 지역 변수(즉, 숨겨진 전역 변수)입니다. 이것을 어휘적/동적 범위 지정이라고 하며, 현재까지 대부분의 언어에서 이를 사용합니다.
이를 설명하기 위해 또 다른 테스트 프로그램을 작성했습니다.
if (( $# == 0 )); then
# run this script with specified shells
bash -c "$0 -c"
ksh93 -c "$0 -c"
exit 0
fi
# which shell am I
readlink /proc/$$/exe
# declarations
local="Dynamic scoping" # i.e. func i view "local" value
global="Lexical/Static scoping" # i.e. func i views "global" value
glob="$global"
# non POSIX declarations
function f { typeset glob="$local"; i; }
function i { echo " $glob"; }
# call
echo " non POSIX ( function f )"
f
# POSIX declations
pf() { typeset glob="$local"; i; }
pi() { echo " $glob"; }
# call
echo " POSIXi ( f() )"
pf
구문적으로 이러한 명령/스크립트는 bash 및 ksh93에서 실행될 수 있습니다.
함수 f는 지역 변수를 정의하고 i를 호출합니다. 어휘 범위의 경우 함수 i는 함수 f의 범위 외부에서 어휘적으로 정의되므로 전역 변수에 액세스할 수 있습니다. 동적 범위에서는 지역 변수가 생성되지 않고 전역 변수만 생성되며 함수 f는 자체 실행 수명 동안에만 값을 설정합니다. 함수 i가 호출되면 함수 f는 여전히 "활성"이므로 전역 변수 value 값은 여전히 함수 f에 할당된 값을 갖습니다. 쉘은 함수 f가 반환될 때만 변경된 값을 복원합니다.
출력은 다음과 같습니다.
/u-blox/gallery/ubx/det/re6_64/8.0/bin/bash
non POSIX ( function f )
Dynamic scoping
POSIXi ( f() )
Dynamic scoping
/bin/ksh93
non POSIX ( function f )
Lexical/Static scoping
POSIXi ( f() )
Dynamic scoping
이제 문제는 bash에서 어휘 범위를 구현하는 방법입니다. ;-)