쉘 스크립트를 네임스페이스(바람직하게는 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
범위를 지정 하여 현재 작업 디렉터리의 범위를 상당히 안정적으로 결정할 수도 있습니다. 매우 열심히 노력하지만 이에 대한 보장은 없습니다. 설명자를 유지하며 래핑 대상이 반환될 때 설명자를 유지합니다 . 현재 작업 디렉터리가 그 동안 변경된 경우에도 최소한 원래 작업 디렉터리로 다시 변경할 수는 있지만 이 경우 보기 흉한 오류가 발생합니다. 그리고 설명자를 유지하기 때문에 정상적인 커널이 루트 장치의 마운트 해제를 허용해서는 안 된다고 생각합니다.$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
그다지 유용하지 않고 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
}