대시(및 bash zsh 및 기타 쉘)에서 local
이 명령이 수행하는 작업은 변수의 범위를 해당 함수(및 경우에 따라 하위 함수)로 제한하는 것입니다. 이를 통해 변수를 이 함수로 제한할 수 있습니다.내부 구조만(어떤 경우에는 하위 함수 호출).
예를 들어:
testlocal(){
local IFS
IFS=123
echo "internal IFS = $IFS"
testdescend
}
testdescend(){
echo "descended IFS = $IFS"
}
IFS=abc
testlocal
echo "external IFS = $IFS"
대시(bash 및 zsh 포함)에서 실행하면 다음 출력이 생성됩니다.
$ dash ./script
internal IFS = 123
descended IFS = 123
external IFS = abc
이는 IFS가 여전히 존재한다는 것을 의미합니다.현지의함수(및 자손)에.
그러나 ksh는 다르며 다음을 허용하지 않습니다 local
.
$ ksh ./script
./mt2[3]: local: not found [No such file or directory]
"local"이라는 단어를 "typeset"으로 변경하면 스크립트가 ksh에서 거의 실행 가능하지만 dash에서는 인식되지 않습니다 typeset
.
function
그리고 ksh에서 범위를 동적으로 만들려면(호출된 함수까지) 함수는 다음 단어를 사용하여 정의되어야 합니다.
function testlocal {
typeset IFS
IFS=123
echo "internal IFS = $IFS"
testdescend
}
이는 Dash의 이식성을 더욱 복잡하게 만듭니다.
문제는 원본 스크립트가 ksh에서도 작동하도록 하려면 어떻게 해야 합니까? 어떤 쉘이 스크립트를 실행하고 있는지 테스트하는 것을 피할 수 있습니까?
답변1
local
ksh88과 모든 클론은 동적 범위 지정을 수행하며 최소한 1990년 ksh88과 1994년 pdksh
(그리고 bash
많은 ksh88 API가 구현된 1989년(현재)) ksh88 이후로 이를 지원해 왔습니다.
당신이 ksh
언급하고 있는 것은 ksh93
약간 다르고 호환되지 않는 API를 가진 David Korn이 처음부터 새로 구현한 것입니다.
수십 년 된 API(ksh93 구현을 제외한 모든 구현) 를 참조하는 ksh93
것보다 이 쉘을 API라고 부르는 것이 더 좋습니다 . 2000년 이후 몇 년이 지나서야 그 코드가 오픈 소스로 공개되어 널리 사용되었습니다.ksh
ksh
ksh88
ksh93
ksh93의 typeset
유일한 기능변화 없는범위 지정. POSIX는 동적으로 범위가 지정되므로 ksh88에 대해 typeset
/ 지정을 더 이상 사용하지 않습니다 local
(C와 같은 대부분의 언어가 이를 수행하더라도).변화 없는범위 지정,동적범위 지정은 서브셸이나 환경을 통해 얻는 효과이므로 셸에서 더 자연스럽습니다. 이는 ksh93이 정적으로 다시 작성된 이유를 설명합니다.
나중에 대부분의 다른 쉘은 ksh88의 범위 지정을 구현했으므로 이제 ksh93은 예외입니다. 아이러니하게도 POSIX에 대한 유일한 합리적인 옵션은 동적 범위를 지정하는 것입니다. David Korn은 처음에는 ksh93에서 동적 범위 지정을 구현하는 것을 거부했지만 local
POSIX 메일링 목록에서 /buildin 키워드를 사용하는 것을 고려할 수 있다고 밝혔 지만 이것이 완전히 구현되기 전에 은퇴했습니다.
AT&T의 ksh93v-beta 및 최종 버전은 실험적인 "bash" 모드(실제로는 기본적으로 활성화됨)를 사용하여 컴파일할 수 있습니다. 이 모드는 로 호출될 때 동적 범위 지정( with local
및 포함을 포함한 함수 형식 typeset
)을 수행합니다.ksh93
bash
bash
이 모드는 ksh2020에서 기본적으로 비활성화되어 있습니다.하지만local
/alias는 bash 모드가 컴파일되지 않은 경우에도 declare
유지됩니다 .typeset
(여전히 정적 범위가 있지만).
이제 베타 버전과 bash 모드를 제쳐두면 ksh93오직ksh 스타일 구문( )을 사용하여 선언된 함수 내에서만 정적 범위 지정을 수행합니다 function name { code; }
. Bourne 스타일 구문( )을 사용하여 선언된 함수가 f() command
범위를 적용하지 않기 때문에 혼란스럽습니다.별말씀을요. 이는 호출된 소스 파일이나 ksh 함수를 사용하는 것과 같습니다 . name [args]
. 여기서는 typeset
함수 범위에 새 변수가 선언되지 않고(함수에는 범위가 없음) 현재 범위의 변수 유형만 업데이트하며, 변수 유형은 전역 범위 또는 ksh의 범위가 됩니다. -style 함수는 Bourne 스타일이 (결국) ksh 스타일 함수에서 호출되는 경우입니다.
Bourne 스타일 함수의 코드는 호출 시 삽입/복사-붙여넣기/소스처럼 실행됩니다.
var=global
function ksh_function {
typeset var=private
echo "ksh1: $var"
bourne_function
echo "ksh2: $var"
other_ksh_function other
echo "ksh3: $var"
. other_ksh_function other_invoked_with_dot
echo "ksh4: $var"
}
bourne_function() {
typeset var=set-from-bourne-function
}
function other_ksh_function {
echo "other: $var"
var=$1
}
ksh_function
echo "global: $var"
다음을 제공합니다:
ksh1: private
ksh2: set-from-bourne-function
other: global
ksh3: set-from-bourne-function
other: set-from-bourne-function
ksh4: other_invoked_with_dot
global: other
ksh93에서는 다음과 같이 서브쉘을 사용하거나 변수 스택을 직접 구현하는 것 외에는 동적 범위 지정이 불가능합니다.locvar
개념의 증거아니면 당신출구모든 명령에 전달되는 변수(환경을 통한 외부 명령을 포함하여 ksh 스타일 함수를 사용하여 선언된 함수 포함)
testlocal
함수 에만(이 아닌) 로컬 범위가 필요한 특정 경우에는 testdescend
설명된 대로 shdef
+ 메서드를 사용할 수 있습니다.kshdef
거기아니면 다음과 같이 하세요:
case $KSH_VERSION in
(*" 93"*)
fn_with_local_scope() {
alias local=typeset
eval "function $1 {
$(cat)
}"
}
;;
(*)
fn_with_local_scope() {
eval "$1() {
$(cat)
}"
}
;;
esac
그런 다음 함수를 다음과 같이 선언하십시오.
fn_with_local_scope testlocal << '}'
local IFS
IFS=123
echo "internal IFS = $IFS"
testdescend
}
testdescend(){
echo "descended IFS = $IFS"
}
IFS=abc
testlocal
echo "external IFS = $IFS"
(그리고 local
선언된 함수 내에서만 fn_with_local_scope
).
이것은 만든다
internal IFS = 123
descended IFS = 123
external IFS = abc
모든 셸에서(지원을 위해서는 최신 버전 yash
(2.48 이상) 이 필요합니다 local
.)
또는 로컬 변수를 내보내는 데 동의하는 경우(ksh93만 해당):
case $KSH_VERSION in
(*" 93"*)
fn() {
alias local='typeset -x'
eval "function $1 {
$(cat)
}"
}
;;
(*)
fn() {
eval "$1() {
$(cat)
}"
}
;;
esac
fn testlocal << '}'
local IFS
IFS=123
echo "internal IFS = $IFS"
testdescend
}
fn testdescend << '}'
echo "descended IFS = $IFS"
}
IFS=abc
testlocal
echo "external IFS = $IFS"
이제 C 또는 ksh93과 같은 정적 범위의 언어를 사용하여 유사한 작업을 수행하려면 다음을 수행합니다.
function testlocal {
typeset IFS
IFS=123
echo "internal IFS = $IFS"
testdescend "$IFS"
}
function testdescend {
typeset IFS="$1" # explicitly get the value $IFS from the caller
echo "descended IFS = $IFS"
}
내 생각에는 이것이 더 나은 디자인이고 코드는 동적 범위 지정을 수행하는 셸에서 잘 작동할 것입니다(여전히 다른 함수 정의 구문을 해결해야 합니다).
추가 자료:
답변2
나는 bash(내 의견으로는 더 나은 배열 구문)를 선호하지만 때로는 ksh 스크립트를 실행하므로 편리할 때 다음을 수행하려고 합니다.
항상 function func_name { body; }
구문을 사용하십시오.
위 내용을 바탕으로 local
첫 번째 함수 앞에 키워드:를 사용하려면 다음을 사용하세요.
# teach ksh 93 about local
case "$KSH_VERSION" in *' 93'*) alias local='typeset -x' ;; esac
함수 내에서 함수 이름을 얻으려면 다음을 사용하십시오.
local MYNAME="${FUNCNAME[0]:-$0}"