source
쉘 스크립트를 네임 스페이스, 바람직하게는 bash 쉘 스크립트로 넣을 수 있는 방법이 있습니까? 하지만이 기능이 있고 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
답변
에서 man ksh
A를 시스템에 ksh93
설치 …
- 네임 스페이스
namespace
변수를 수정하거나 새 변수를 작성 하는 명령 목록의 일부로 실행되는 명령 및 함수 는 이름이 앞에 오는 식별자로 지정된 이름 공간의 이름 인 새 변수를 작성하십시오.
. 이름이 name 인 변수가 참조되면 먼저를 사용하여 검색됩니다.identifier.name
.- 마찬가지로 네임 스페이스 목록의 명령으로 정의 된 함수는 네임 스페이스 이름 앞에 a가옵니다
.
.- 네임 스페이스 명령 목록에 명령이 포함 된 경우
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!
답변
나는 로컬의에서 쉘 내장 또는 함수 네임 스페이스에 사용되는 수있는 POSIX 쉘 기능을 작성했습니다 ksh93
, dash
, mksh
, 또는 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
반면에 일반 내장 은 서브 쉘 환경 에서 실행되도록 지정되어 있는데, 이는 반드시 다른 프로세스 의 환경을 의미하는 것은 아니며 단지 기본적으로 구별 할 수 있어야한다는 것입니다. 정규 내장을 호출 한 결과는 항상 비슷한 능력을 가진 $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
명령은 쉘 함수를 호출 할 수 없으므로 일반 내장에서와 같이 특수한 처리 방법을 사용할 수 없습니다. 그것은 또한 spec’d이다. 실제로 스펙의 주요 유틸리티는 command
다른 명령에 대해 이름 지정된 랩퍼 쉘 함수 내에서이를 사용하여 함수를 호출하지 않기 때문에 자체 재귀없이 다른 명령을 호출 할 수 있다는 것입니다. 이처럼 :
cd(){ command cd -- "$1"; }
command
거기에서 사용하지 않으면 cd
함수는 자기 재귀를 위해 거의 확실하게 segfault합니다.
그러나 특수 내장을 호출 할 수있는 일반 내장으로서 서브 쉘 환경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
위 의 단어가 비어있는 경우에만 단어로 인식됩니다 $PATH
. 게다가 명령 행에 이름이 쉘 변수를 로컬 범위 지정, 그것은 또한 같은 하나의 소문자 알파벳 이름과 다른 표준 것들의 목록, 모든 변수를 로컬 스코프 $PS3
, $PS4
, $OPTARG
, $OPTIND
, $IFS
, $PATH
, $PWD
, $OLDPWD
일부 다른 사람.
그리고 네, 로컬 범위 지정에 의해 $PWD
및 $OLDPWD
이후 변수를 명시 적 cd
으로 보내고 $OLDPWD
과 $PWD
그것을 할 수있는 매우 안정적으로 범위를 현재 작업 디렉토리뿐만 아니라. 꽤 열심히 시도하지만 보장되지는 않습니다. 7<.
랩 대상이 리턴 할 때와 그 시점에 설명자를 보유합니다 cd -P /dev/fd/7/
. 현재 작업 디렉토리가 unlink()
중간에 있으면 디렉토리 로 다시 변경해야하지만 그 경우 추악한 오류가 발생합니다. 그리고 설명자를 유지 관리하기 때문에 제정신 커널이 루트 장치를 마운트 해제해야한다고 생각하지 않습니다 (???) .
또한 쉘 옵션의 범위를 로컬로 지정하고 랩핑 된 유틸리티가 리턴 될 때 찾은 상태로 복원합니다. 그것은 취급 $OPTS
그것이 처음의 값을 할당 자신의 범위에 복사본을 유지하는에 특별히 $-
. 명령 행에서 모든 지정을 처리 한 후에는 set -$OPTS
랩 대상을 호출하기 직전에 수행됩니다 . 이런 식으로 -$OPTS
명령 행에서 정의하면 랩 대상의 쉘 옵션을 정의 할 수 있습니다. 대상이 반환되면 set +$- -$OPTS
자체 복사본 $OPTS
(명령 줄 정의의 영향을받지 않음) 을 사용하여 원래 상태로 복원합니다.
물론, returrn
랩 대상 또는 인수를 통해 호출자 가 함수 에서 명시 적으로 기능을 종료하는 것을 막을 수는 없습니다 . 그렇게하면 상태 복원 / 정리가 시도되지 않습니다.
모든 것을하기 위해서는 세 개의 eval
깊이가 필요합니다 . 먼저 로컬 범위로 랩핑 한 다음 내부 범위에서 인수를 읽고 유효한 쉘 이름을 검증하고 그렇지 않은 경우 오류로 종료합니다. 모든 인수가 유효하고 결국 하나의 원인 인 경우 command -v "$1"
true를 반환하는 (리콜 : $PATH
이 시점에서 비어) 는 것이다 eval
명령 줄 정의하고 랩 대상에 남아있는 모든 인수를 전달할 이의 특별한 경우를 무시하지만 ( ns
– 저 같으면 때문에 매우 유용하지 않으며 eval
깊이는 3 보다 깊습니다 .
기본적으로 다음과 같이 작동합니다.
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
명령 행에서 직접 설정하지 않으면 유효한 쉘 이름 만 사용됩니다 . 분할 변수에 glob 문자가 있더라도 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
}