私は、ローカルのいずれかでシェル組み込みまたは関数の名前空間に使用される可能性がありますPOSIXシェル関数書かれているksh93、dash、mksh、またはbash (私は個人的にこれらのすべてで動作するようにそれを確認しているので、具体的という名前を)。私がそれをテストしたシェルのうち、それはでの私の期待に応えることができなかっただけで、それがyashでまったく機能するとはまったく思っていませんでしたzsh。私はテストしませんでしたposh。poshしばらく前から希望をあきらめ、しばらくはインストールしていません。多分それはposh... で動作します
私が仕様を読んだことで、基本的なユーティリティの指定された動作を利用しているため、POSIXであると言いますが、確かに、この点に関して仕様は曖昧であり、少なくとも1人が明らかに私に反対しています。一般的に私はこれに同意しませんでしたが、最終的に自分のエラーであることがわかりました。また、今回も仕様について間違っているかもしれませんが、さらに彼に質問したところ、彼は返答しませんでした。
しかし、先ほど述べたように、これは前述のシェルで確実に機能し、基本的には次のように機能します。
some_fn(){ x=3; echo "$x"; }
x=
x=local command eval some_fn
echo "${x:-empty}"
3
empty
commandコマンドは、基本的に利用可能なユーティリティと事前の一つとして指定されている$PATH「Dの組み込み関数。指定された関数の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
...上記の両方のコマンドライン割り当ての動作は仕様により正しいです。両方のエラー条件の動作も正しく、実際には仕様からほぼ完全に複製されています。関数のコマンドラインまたは特別な組み込みコマンドの前に付けられた割り当ては、現在のシェル環境に影響を与えるように指定されます。同様に、リダイレクトエラーは、いずれかを指すと致命的と指定されます。commandこれらの場合の特別な組み込みの特別な扱いを抑制するために指定され、リダイレクトの場合は実際には仕様の例で示されています。
command一方、などの通常のビルトインは、サブシェル環境で実行するように指定されています。これは必ずしも別のプロセスのビルトインを意味するのではなく、基本的に1つと区別できないはずです。通常のビルトインを呼び出した結果は、同様の機能を持つ$PATH'dコマンドから得られるものに常に似ているはずです。など...
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関数はほぼ間違いなく自己再帰のために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がラップターゲットまたはその引数を使用して、なんらかの方法で明示的に関数から抜け出すことを妨げるものは何もありません。そうすることで、他の方法で試行する状態の復元/クリーンアップを防ぐことができます。
それをすべて行うには、3 evalの深さに行く必要があります。まず自身をローカルスコープでラップし、次に、内部から引数を読み取り、有効なシェル名があるかどうかを検証し、そうでない場合はエラーで終了します。すべての引数が有効であり、最終的に1原因である場合はcommand -v "$1"trueを返すために(リコール:$PATHこの時点では空である)、それは意志evalのコマンドライン定義し、ラップターゲットに残りのすべての引数をオフに渡し、それがために、特別なケースを無視しても(ns-それwouldn理由それほど有用ではありません。3 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 (???)が、その全ての補助、および主に発光から救うためにだけ使用します不要な出力など、エッジケースでも同様です。そして、それはそれがどのように機能するかです。ネストされたユーティリティでラップされたユーティリティを呼び出す前に独自のローカルスコープを設定するため、これらのことを実行できます。
私evalsはここで非常に注意しようとするので、それは長いです-3 つは難しいです。しかし、それであなたはできる:
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展開を避けるために接頭辞w /が付いています。
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
}
( easiest thing ever )です。しかし、それはあなたが望んでいることとは全く異なります。私はあなたが行うことができると思います( stuff in subshell; exec env ) | sed 's/^/namespace_/'し、eval親シェルでの結果が、それは厄介なのようなものです。