쉘 네임스페이스

쉘 네임스페이스

쉘 스크립트를 네임스페이스(바람직하게는 bash 쉘 스크립트)에 넣을 수 있는 방법이 있습니까 source? 하지만 다른 쉘에 이 기능이 있고 bash에는 없으면 살펴보겠습니다.

예를 들어, 이름 충돌을 방지하기 위해 "이미 정의된 기호(변수 이름, 함수 이름, 별칭)와 충돌하지 않도록 정의된 모든 기호에 접두사를 붙입니다" 또는 기타 기능을 의미합니다.

source둘 다 네임스페이스 ( 스타일) 를 지정할 수 있는 솔루션이 있다면 NodeJS가장 좋을 것입니다 .

샘플 코드:

$ echo 'hi(){ echo Hello, world; }' > english.sh
$ echo 'hi(){ echo Ahoj, světe; }' > czech.sh
$ . english.sh
$ hi
 #=> Hello, world
$ . czech.sh #bash doesn't even warn me that `hi` is being overwritten here
$ hi
 #=> Ahoj, světe
#Can't use the English hi now
#And sourcing the appropriate file before each invocation wouldn't be very efficient 

답변1

man ksh시스템 에서 ...ksh93

  • 네임스페이스
    • 변수를 수정하거나 새 변수를 생성하는 명령 목록의 일부로 실행되는 명령 및 함수는 namespace이름이 이전 식별자에 의해 제공된 네임스페이스의 이름인 새 변수를 생성합니다... name이라는 변수를 참조할 때 먼저 다음을 사용하십시오..identifier.name.
    • 마찬가지로 네임스페이스 목록의 명령으로 정의된 함수는 로 시작하는 네임스페이스 이름을 사용하여 생성됩니다 ..
    • 네임스페이스 명령 목록에 namespace명령이 포함된 경우 생성된 변수 및 함수의 이름은 변수 또는 함수 이름으로 구성되며, 각 이름 앞에는 식별자 목록이 옵니다 .. 네임스페이스 외부에서는 네임스페이스 이름 앞에 이름을 추가하여 네임스페이스 내에서 생성된 변수나 함수를 참조할 수 있습니다.
    • 기본적으로 .sh다음으로 시작하는 변수는sh네임스페이스.

그리고 설명을 위해 ksh93쉘에 할당된 모든 일반 쉘 변수에 대해 기본적으로 제공되는 네임스페이스에 적용되는 개념은 다음과 같습니다. 다음 예에서는discipline.get이 함수는 쉘 변수 에 대해 지정된 메소드 로 작동합니다 $PS1. 각 셸 변수에는 기본적으로 최소한 기본 get, set및 메서드가 append포함 된 자체 네임스페이스가 있습니다 unset. 다음 함수를 정의한 후 쉘에서 변수를 참조할 때마다 $PS1화면 상단에 출력이 그려집니다...date

function PS1.get {
    printf "\0337\33[H\33[K%s\0338" "${ date; }"
}

()(또한 위의 명령 대체에는 하위 쉘이 없다는 점에 유의하세요)

기술적으로 말하자면,네임스페이스그리고주제아니요정확히같은 것(왜냐하면주제특정 항목에 전역적으로 또는 로컬로 적용하도록 정의할 수 있습니다.네임스페이스)이지만 둘 다 쉘 데이터 유형 개념화의 중요한 부분입니다 ksh93.

구체적인 예를 다루려면 다음을 수행하십시오.

echo 'function hi { echo Ahoj, světe\!;  }' >  czech.ksh
echo 'function hi { echo Hello, World\!; }' >english.ksh
namespace english { . ./english.ksh; }
namespace czech   { . ./czech.ksh;   }
.english.hi; .czech.hi

Hello, World!
Ahoj, světe!

...또는...

for ns in czech english
do  ".$ns.hi"
done

Ahoj, světe!
Hello, World!

답변2

ksh93, dash또는 mksh중 하나에서 쉘 내장 또는 함수를 로컬로 네임스페이스하는 데 사용할 수 있는 POSIX 쉘 함수를 작성했습니다.bash (제가 개인적으로 모든 제품에 효과가 있음을 확인했기 때문에 특별히 이름을 붙였습니다). 내가 테스트한 쉘에서는 내 기대에 미치지 못했을 뿐이고, yash작동할 것이라고는 전혀 예상하지 못했습니다 zsh. 나는 그것을 테스트하지 않았습니다 posh. 나는 이전에 어떤 희망도 포기 posh하고 한동안 설치하지 않았습니다. 아마도 posh...?

사양을 읽은 결과 기본 유틸리티의 지정된 동작을 사용하기 때문에 POSIX라고 말합니다. 그러나 분명히 이 점에 있어서 사양은 모호하며 적어도한 사람분명히 내 의견에 동의하지 않습니다. 일반적으로 말해서 나는 이 문제에 대해 문제를 제기하고 결국 오류가 내 자신의 것이고 아마도 이번에도 사양에서 틀렸을 것이라는 것을 알아냈지만 내가 그에게 더 물었을 때 그는 대답하지 않았습니다.

그러나 앞서 말했듯이 이는 위의 셸에서 작동하며 기본적으로 다음과 같이 작동합니다.

some_fn(){ x=3; echo "$x"; }
x=
x=local command eval some_fn
echo "${x:-empty}"

3
empty

command명령은 사용 가능한 기본 유틸리티 및 사전 내장 명령 중 하나로 지정됩니다 $PATH. 지정된 기능 중 하나는 특수 내장 유틸리티가 호출될 때 자체 환경에 래핑하는 것입니다.

{       sh -c ' x=5 set --; echo "$x"
                x=6 command set --; echo "$x"
                exec <"";  echo uh_oh'
        sh -c ' command exec <""; echo still here'
}

5
5
sh: 3: cannot open : No such file
sh: 1: cannot open : No such file
still here

...사양에 따르면 위의 두 명령줄 할당이 모두 올바르게 작동합니다. 두 오류 조건의 동작도 정확하며 실제로 사양과 거의 정확히 동일합니다. 지정된 함수 또는 특수 내장 함수에 명령줄 접두어를 할당하면 현재 쉘 환경에 영향을 미칩니다. 마찬가지로 리디렉션 오류는 이러한 오류를 가리킬 때 치명적인 오류로 지정됩니다. command이러한 경우 특수 내장 기능의 특수 처리를 억제하도록 지정되었으며 리디렉션 사례는 실제로 사양의 예를 통해 설명됩니다.

command반면에 일반 내장 함수(예:)는 다음과 같이 지정됩니다.서브쉘 환경- 반드시 그런 뜻은 아니다.다른 프로세스단, 근본적으로 하나와 구별할 수 없어야 합니다. 일반 내장 함수를 호출한 결과는 항상 비슷한 기능의 'd 명령에서 $PATH얻은 결과 와 유사해야 합니다. 그래서...

na=not_applicable_to_read
na= read var1 na na var2 <<"" ; echo "$var1" "$na" "$var2"
word1 other words word2

word1 not_applicable_to_read word2

그러나 이 command명령은 쉘 함수를 호출할 수 없으므로 일반 내장 함수와 동일한 방식으로 사용할 수 없으므로 특별한 처리가 의미가 없습니다. 이것도 지정되어 있습니다. 실제로 사양에 따르면 이 함수의 주요 용도는 command함수를 호출하지 않기 때문에 자체 재귀적일 필요 없이 다른 명령을 호출하기 위해 다른 명령으로 명명된 래퍼 셸 함수에서 사용할 수 있다는 것입니다. 이와 같이:

cd(){ command cd -- "$1"; }

command이 기능을 사용 하지 않으면 cd거의 확실하게 자체 재귀 세그폴트가 발생합니다.

하지만 특수 내장 함수를 호출할 수 있는 일반 내장 함수로는 다음과 같은 용도 command로 사용할 수 있습니다.서브쉘 환경. 따라서 현재 정의된 셸 상태는 현재 셸에서 지속될 수 있지만(확실히 그렇습니다. read) $var1적어도 $var2명령줄 정의의 결과는 그렇지 않아야 합니다.

간단한 명령

명령 이름이 결과로 나오지 않거나 명령 이름이 특수 내장 함수인 경우 변수 할당은 현재 실행 환경에 영향을 미칩니다. 그렇지 않으면 변수 할당을 명령 실행 환경으로 내보내야 하며 현재 실행 환경에 영향을 주어서는 안 됩니다.

command이제 동시에 일반 내장이 되는 것이 가능한가요 ?그리고특수 내장 기능을 직접 호출하는 것은 내가 알지 못하는 명령줄 정의와 관련된 일종의 우연한 취약점이지만 언급된 쉘 중 적어도 4개는 네임 command스페이스를 존중한다는 것을 알고 있습니다.

command쉘 함수를 직접 호출할 수는 없지만할 수 있는시연된 것으로 호출 eval되므로 간접적으로 수행할 수 있습니다. 그래서 저는 이 개념을 바탕으로 네임스페이스 래퍼를 만들었습니다. 다음과 같은 매개변수 목록이 필요합니다.

ns any=assignments or otherwise=valid names which are not a command then all of its args

...와는 별개로command위의 단어는 null이 발견될 경우에만 하나로 인식됩니다 $PATH. 명령줄에 명명된 로컬 범위의 셸 변수 외에도 단일 소문자 이름과 , $PS3, $PS4, $OPTARG, $OPTIND, $IFS$PATH기타 몇 가지 기타 표준 이름 목록 $PWD이 있는 모든 변수의 범위를 로컬로 지정합니다.$OLDPWD

예, 합계 변수의 범위를 로컬로 지정 하고 명시적 으로 $PWD범위를 지정 하여 현재 작업 디렉터리의 범위를 상당히 안정적으로 결정할 수도 있습니다. 매우 열심히 노력하지만 이에 대한 보장은 없습니다. 설명자를 유지하며 래핑 대상이 반환될 때 설명자를 유지합니다 . 현재 작업 디렉터리가 그 동안 변경된 경우에도 최소한 원래 작업 디렉터리로 다시 변경할 수는 있지만 이 경우 보기 흉한 오류가 발생합니다. 그리고 설명자를 유지하기 때문에 정상적인 커널이 루트 장치의 마운트 해제를 허용해서는 안 된다고 생각합니다.$OLDPWDcd$OLDPWD$PWD7<.cd -P /dev/fd/7/unlink()(???).

또한 로컬 범위에서 셸 옵션을 설정하고 이러한 옵션을 래핑하는 유틸리티가 반환될 때 발견된 상태로 복원합니다. 특별한 $OPTS처리는 자체 범위 내에서 복사본을 유지하고 처음에 복사본에 값을 할당한다는 것입니다 $-. set -$OPTS랩 대상을 호출하기 전에 명령줄의 모든 할당이 처리된 후 이 작업을 수행합니다 . 이 방법으로 -$OPTS명령줄에서 정의한 경우 래핑 대상에 대한 셸 옵션을 정의할 수 있습니다. 대상이 돌아오면 set +$- -$OPTS자신의 복사본도 함께 가져 옵니다.$OPTS (명령줄 정의의 영향을 받지 않음)모든 것을 원래 상태로 복원합니다.

returrn물론, 대상이나 해당 인수를 래핑하여 호출자가 어떤 방식으로든 함수를 명시적으로 종료하는 것을 막을 수는 없습니다 . 그렇게 하면 상태 복원/정리를 시도하는 것이 방지됩니다.

그러기 위해서는 세 가지 마음을 깊이 파고 들어가야 합니다 eval. 먼저 로컬 범위에서 자신을 래핑한 다음 내부에서 매개변수를 읽고 해당 매개변수가 유효한지 확인한 후 잘못된 셸 이름이 발견되면 오류와 함께 종료됩니다. 모든 매개변수가 유효하고 마지막으로 하나의 매개변수가 있는 경우 command -v "$1"true를 반환합니다.(참고: $PATH현재는 비어 있음)eval명령줄 정의를 취하고 나머지 모든 인수를 래퍼 대상에 전달합니다.(-의 특별한 경우는 무시하지만 ns그다지 유용하지 않고 3s eval는 이미 충분히 깊기 때문에).

기본적으로 다음과 같이 작동합니다.

case $- in (*c*) ... # because set -c doesnt work
esac
_PATH=$PATH PATH= OPTS=$- some=vars \
    command eval LOCALS=${list_of_LOCALS}'
        for a do  i=$((i+1))          # arg ref
              if  [ "$a" != ns ]  &&  # ns ns would be silly
                  command -v "$a" &&
              !   alias "$a"          # aliases are hard to run quoted
        then  eval " PATH=\$_PATH OTHERS=$DEFAULTS $v \
                     command eval '\''
                             shift $((i-1))         # leave only tgt in @
                             case $OPTS in (*different*)
                                  set \"-\${OPTS}\" # init shell opts 
                             esac
                             \"\$@\"                # run simple command
                             set +$- -$OPTS "$?"    # save return, restore opts
                     '\''"
              cd -P /dev/fd/7/        # go whence we came
              return  "$(($??$?:$1))" # return >0 for cd else $1
        else  case $a in (*badname*) : get mad;;
              # rest of arg sa${v}es
              esac
        fi;   done
    ' 7<.

c몇 가지 다른 리디렉션이 있으며 일부 쉘이 삽입된 다음 $-옵션으로 받아들이기를 거부하는 방식과 관련된 몇 가지 이상한 테스트가 있습니다.set (???), 그러나 이는 모두 보조적이며 주로 원치 않는 출력 및 극단적인 경우 유사한 출력을 방출하는 것을 방지하는 데 사용됩니다. 이것이 작동하는 방식입니다. 중첩된 유틸리티를 호출하기 전에 자체 로컬 범위를 설정하기 때문에 이러한 작업을 수행할 수 있습니다.

여기서는 매우 조심해야 하기 때문에 길어요. 3개 evals는 어렵습니다. 하지만 그것으로 당신은 할 수 있습니다:

ns X=local . /dev/fd/0 <<""; echo "$X" "$Y"
X=still_local
Y=global
echo "$X" "$Y"

still_local global
 global

한 단계 더 나아가 래퍼 유틸리티의 로컬 범위 이름을 지속적으로 지정하는 것은 어렵지 않습니다. 작성된 대로라도 $LOCALS래퍼 유틸리티 환경에 정의된 모든 이름의 공백으로 구분된 목록으로 구성된 래퍼 유틸리티용 변수가 이미 정의되어 있습니다.

좋다:

ns var1=something var2= eval ' printf "%-10s%-10s%-10s%s\n" $LOCALS '

...완전히 안전합니다. $IFS기본값으로 정리되었으며 $LOCALS명령줄에서 직접 설정하지 않는 한 유효한 쉘 이름만 입력할 수 있습니다. 분할 변수에 전역 문자가 있을 수 있더라도 OPTS=f명령줄에서 래핑 유틸리티를 설정하여 확장을 비활성화할 수 있습니다. 어떤 경우 에라도:

LOCALS    ARG0      ARGC      HOME
IFS       OLDPWD    OPTARG    OPTIND
OPTS      PATH      PS3       PS4
PWD       a         b         c
d         e         f         g
h         i         j         k
l         m         n         o
p         q         r         s
t         u         v         w
x         y         z         _
bel       bs        cr        esc
ht        ff        lf        vt
lb        dq        ds        rb
sq        var1      var2      

이것이 기능입니다. 확장을 \피하기 위해 모든 명령에는 접두사가 붙습니다 alias.

ns(){  ${1+":"} return
       case  $- in
       (c|"") ! set "OPTS=" "$@"
;;     (*c*)  ! set "OPTS=${-%c*}${-#*c}" "$@"
;;     (*)      set "OPTS=$-" "$@"
;;     esac
       OPTS=${1#*=} _PATH=$PATH PATH= LOCALS=     lf='
'      rb=\} sq=\' l= a= i=0 v= __=$_ IFS="       ""
"      command eval  LOCALS=\"LOCALS \
                     ARG0 ARGC HOME IFS OLDPWD OPTARG OPTIND OPTS     \
                     PATH PS3 PS4 PWD a b c d e f g h i j k l m n     \
                     o p q r s t u v w x y z _ bel bs cr esc ht ff    \
                     lf vt lb dq ds rb sq'"
       for a  do     i=$((i+1))
              if     \[ ns != "$a" ]         &&
                     \command -v "$a"  >&9   &&
              !      \alias "${a%%=*}" >&9 2>&9
              then   \eval 7>&- '\'    \
                     'ARGC=$((-i+$#))  ARG0=$a      HOME=~'           \
                     'OLDPWD=$OLDPWD   PATH=$_PATH  IFS=$IFS'         \
                     'OPTARG=$OPTARG   PWD=$PWD     OPTIND=1'         \
                     'PS3=$PS3 _=$__   PS4=$PS4     LOCALS=$LOCALS'   \
                     'a= b= c= d= e= f= g= i=0 j= k= l= m= n= o='     \
                     'p= q= r= s= t= u= v= w= x=0 y= z= ht=\   '      \
                     'cr=^M bs=^H ff=^L vt=^K esc=^[ bel=^G lf=$lf'   \
                     'dq=\" sq=$sq ds=$ lb=\{ rb=\}' \''"$v'          \
                            '\command eval       9>&2 2>&- '\'        \
                                   '\shift $((i-1));'                 \
                                   'case \${OPTS##*[!A-Za-z]*} in'    \
                                   '(*[!c$OPTS]*) >&- 2>&9"'\'        \
                                   '\set -"${OPTS%c*}${OPTS#*c}"'     \
                                   ';;esac; "$@" 2>&9 9>&-; PS4= '    \
                                   '\set  +"${-%c*}${-#*c}"'\'\"      \
                                          -'$OPTS \"\$?\"$sq";'       \
              '             \cd -- "${OLDPWD:-$PWD}"
                            \cd -P  ${ANDROID_SYSTEM+"/proc/self/fd/7"} /dev/fd/7/
                            \return "$(($??$?:$1))"
              else   case   ${a%%=*}      in
                     ([0-9]*|""|*[!_[:alnum:]]*)
                            \printf "%s: \${$i}: Invalid name: %s\n" \
                            >&2    "$0: ns()"   "'\''${a%%=*}'\''"
                            \return 2
              ;;     ("$a") v="$v $a=\$$a"
              ;;     (*)    v="$v ${a%%=*}=\${$i#*=}"
              ;;     esac
                     case " $LOCALS " in (*" ${a%%=*} "*)
              ;;     (*)    LOCALS=$LOCALS" ${a%%=*}"
              ;;     esac
              fi
       done'  7<.    9<>/dev/null
}

관련 정보