ksh bash 및 zsh를 대시하기 위해 기본 명령을 이식할 수 있는 방법이 있습니까?

ksh bash 및 zsh를 대시하기 위해 기본 명령을 이식할 수 있는 방법이 있습니까?

대시(및 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

localksh88과 모든 클론은 동적 범위 지정을 수행하며 최소한 1990년 ksh88과 1994년 pdksh(그리고 bash많은 ksh88 API가 구현된 1989년(현재)) ksh88 이후로 이를 지원해 왔습니다.

당신이 ksh언급하고 있는 것은 ksh93약간 다르고 호환되지 않는 API를 가진 David Korn이 처음부터 새로 구현한 것입니다.

수십 년 된 API(ksh93 구현을 제외한 모든 구현) 를 참조하는 ksh93것보다 이 쉘을 API라고 부르는 것이 더 좋습니다 . 2000년 이후 몇 년이 지나서야 그 코드가 오픈 소스로 공개되어 널리 사용되었습니다.kshkshksh88ksh93

ksh93의 typeset유일한 기능변화 없는범위 지정. POSIX는 동적으로 범위가 지정되므로 ksh88에 대해 typeset/ 지정을 더 이상 사용하지 않습니다 local(C와 같은 대부분의 언어가 이를 수행하더라도).변화 없는범위 지정,동적범위 지정은 서브셸이나 환경을 통해 얻는 효과이므로 셸에서 더 자연스럽습니다. 이는 ksh93이 정적으로 다시 작성된 이유를 설명합니다.

나중에 대부분의 다른 쉘은 ksh88의 범위 지정을 구현했으므로 이제 ksh93은 예외입니다. 아이러니하게도 POSIX에 대한 유일한 합리적인 옵션은 동적 범위를 지정하는 것입니다. David Korn은 처음에는 ksh93에서 동적 범위 지정을 구현하는 것을 거부했지만 localPOSIX 메일링 목록에서 /buildin 키워드를 사용하는 것을 고려할 수 있다고 밝혔 지만 이것이 완전히 구현되기 전에 은퇴했습니다.

AT&T의 ksh93v-beta 및 최종 버전은 실험적인 "bash" 모드(실제로는 기본적으로 활성화됨)를 사용하여 컴파일할 수 있습니다. 이 모드는 로 호출될 때 동적 범위 지정( with local및 포함을 포함한 함수 형식 typeset)을 수행합니다.ksh93bashbash이 모드는 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}"

관련 정보