シェルスクリプトを名前空間(好ましくは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
。指定された機能の1つは、特別な組み込みユーティリティが呼び出されたときに独自の環境にラップすることです。
{ 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
...仕様によると、上記の2つのコマンドライン割り当ては正しく機能します。両方のエラー条件の動作も正確であり、実際には仕様とほぼ正確に同じです。指定された関数または特殊な組み込み関数にコマンドラインプレフィックスを割り当てると、現在のシェル環境に影響します。同様に、リダイレクトエラーは、これらのエラーを指すときに致命的なエラーとして指定されます。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が見つかった場合にのみ1つとして認識されます$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
もちろん、ターゲットまたはその引数をラップして、呼び出し側が何らかの方法で関数を明示的に終了するのを防ぐことはできません。これにより、状態の復元/クリーンアップを試みることができなくなります。
そのためには、3つの心を深く掘り下げなければなりませんeval
。まず、ローカルスコープで自分自身をラップし、内部でパラメータを読み取り、そのパラメータが有効であることを確認してから、無効なシェル名が見つかった場合はエラーで終了します。すべてのパラメータが有効で、最後に1つのパラメータがある場合はcommand -v "$1"
trueを返します。(注:$PATH
現在は空です)eval
コマンドライン定義を取り、残りのすべての引数をラッパーターゲットに渡します。(-の特別な場合は無視するがあまりns
役に立たず、3seval
はすでに十分に深いので)。
デフォルトでは、次のように動作します。
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
}