変数のスコープをそれが宣言されている関数に制限するPOSIX準拠の方法はありますか?例:
Testing()
{
TEST="testing"
}
Testing
echo "Test is: $TEST"
「Test is:」と出力されます。 declare、local、およびtypesetキーワードについて読みましたが、POSIXビルトインが必要なようではありません。
これは通常、local
キーワードで行われます。これは、ご存知のように、POSIXでは定義されていません。これは参考情報です POSIXに「ローカル」を追加することについての議論 。
ただし、一部のGNU/Linuxディストリビューションでは、/bin/sh
のデフォルトであるdash
(Debian Almquistシェル)がサポートしている、私が知っている最も原始的なPOSIX準拠のシェルでもサポートしています。 FreeBSDとNetBSDは、元のAlmquistシェルであるash
を使用しており、これもサポートしています。 OpenBSDは、/bin/sh
のksh
実装を使用して、これもサポートしています。したがって、Solarisなどの非GNU非BSDシステム、または標準のkshなどを使用するシステムをサポートすることを目的としない限り、local
を使用して問題を回避できます。 (スクリプトの冒頭、Shebang行の下に、厳密にはPOSIX shスクリプトではないことに注意してコメントを付けたいと思うかもしれません。悪さをしないためにです。)以上のことをすべて述べた後、それぞれを確認することをお勧めします。 sh
をサポートするこれらのすべてのlocal
実装のマンページ。正確に機能する方法に微妙な違いがある可能性があるためです。または、local
を使用しないでください。
本当に完全にPOSIXに準拠したい場合、または考えられる問題を台無しにしたくない場合、つまりlocal
を使用しない場合は、いくつかのオプションがあります。 Lars Brinkhoffの答えは健全です。サブシェルで関数をラップするだけです。ただし、これにより他の望ましくない影響が生じる可能性があります。ちなみに、シェル文法(POSIXごと)では、次のことが可能です。
my_function()
(
# Already in a sub-Shell here,
# I'm using ( and ) for the function's body and not { and }.
)
スーパーポータブルにするためにそれを避けるかもしれませんが、一部の古いBourneシェルはPOSIX非準拠でさえあります。 POSIXで許可されていることをお伝えしたいと思います。
別のオプションは、関数本体の最後でunset
変数にすることですが、それはnotもちろん古い値を復元するので、実際に望んでいることではないので、変数の外部にリークする関数内の値。あまり役に立たないと思います。
最後に思いがけない奇抜なアイデアは、local
を自分で実装することです。シェルにはeval
がありますが、これは悪ですが、いくつかの非常識な可能性に道を譲ります。以下は、基本的に古いLispの動的スコープを実装します。最後にいわゆるlet
を使用する必要がありますが、さらなるクールポイントにはlocal
の代わりにキーワードunlet
を使用します。
# If you want you can add some error-checking and what-not to this. At present,
# wrong usage (e.g. passing a string with whitespace in it to `let', not
# balancing `let' and `unlet' calls for a variable, etc.) will probably yield
# very very confusing error messages or breakage. It's also very dirty code, I
# just wrote it down pretty much at one go. Could clean up.
let()
{
dynvar_name=$1;
dynvar_value=$2;
dynvar_count_var=${dynvar_name}_dynvar_count
if [ "$(eval echo $dynvar_count_var)" ]
then
eval $dynvar_count_var='$(( $'$dynvar_count_var' + 1 ))'
else
eval $dynvar_count_var=0
fi
eval dynvar_oldval_var=${dynvar_name}_oldval_'$'$dynvar_count_var
eval $dynvar_oldval_var='$'$dynvar_name
eval $dynvar_name='$'dynvar_value
}
unlet()
for dynvar_name
do
dynvar_count_var=${dynvar_name}_dynvar_count
eval dynvar_oldval_var=${dynvar_name}_oldval_'$'$dynvar_count_var
eval $dynvar_name='$'$dynvar_oldval_var
eval unset $dynvar_oldval_var
eval $dynvar_count_var='$(( $'$dynvar_count_var' - 1 ))'
done
今することができます:
$ let foobar test_value_1
$ echo $foobar
test_value_1
$ let foobar test_value_2
$ echo $foobar
test_value_2
$ let foobar test_value_3
$ echo $foobar
test_value_3
$ unlet foobar
$ echo $foobar
test_value_2
$ unlet foobar
$ echo $foobar
test_value_1
(ちなみに、unlet
には、上記に示されていない便宜上、一度に(異なる引数として)任意の数の変数を与えることができます。
これを家で試したり、子供に見せたり、同僚に見せたり、Freenodeの#bash
に見せたり、POSIXのメンバーに見せたりしないでください。委員会、それをボーン氏に見せないでください、多分それを父マッカーシーの幽霊に見せて笑わせます。あなたは警告されました、そしてあなたは私からそれを学びませんでした。
編集:
どうやら私は殴られて、FreenodeでIRCボットgreybot
(#bash
に属する)を送信すると、コマンド "posixlocal"により、ローカル変数を取得する方法を示す不明瞭なコードが1つ表示されますPOSIX sh。オリジナルは解読が困難だったので、これはややクリーンアップされたバージョンです:
f()
{
if [ "$_called_f" ]
then
x=test1
y=test2
echo $x $y
else
_called_f=X x= y= command eval '{ typeset +x x y; } 2>/dev/null; f "$@"'
fi
}
このトランスクリプトは使用法を示しています。
$ x=a
$ y=b
$ f
test1 test2
$ echo $x $y
a b
したがって、ifフォームのx
ブランチで変数y
およびthen
をローカル変数として使用できます。 else
ブランチでさらに変数を追加できます。最初のリストのvariable=
のように2回追加し、typeset
の引数として1回渡す必要があることに注意してください。 unlet
などは必要ありません(「透過的な」実装です)。名前の変更や過度のeval
は行われません。したがって、全体的にはよりクリーンな実装のようです。
編集2:
typeset
はPOSIXでは定義されておらず、Almquist Shell(FreeBSD、NetBSD、Debian)の実装ではサポートされていません。したがって、上記のハックはこれらのプラットフォームでは機能しません。
最も近いのは、関数本体をサブシェルの中に置くことだと思います。
例えば。これを試して
foo()
{
( x=43 ; echo $x )
}
x=42
echo $x
foo
echo $x
私と一緒に地獄に行きたくなったら、eval
の概念をより詳細に実装しました。
これは自動的に準スコープ変数のアカウントを保持し、より使い慣れた構文で呼び出すことができ、ネストされたスコープを離れるときに変数を(単にnullするのではなく)適切に設定解除します。
ご覧のとおり、あなたはPush_scope
スコープを入力するには、_local
は、準ローカル変数を宣言し、pop_scope
スコープを離れます。使用する _unset
は変数を設定解除し、pop_scope
は、そのスコープに再び戻ったときに、設定を解除します。
your_func() {
Push_scope
_local x="baby" y="you" z
x="can"
y="have"
z="whatever"
_unset z
Push_scope
_local x="you"
_local y="like"
pop_scope
pop_scope
}
意味不明な変数名のサフィックスはすべて、名前の衝突に対して非常に安全です。
# Simulate entering of a nested variable scope
# To be used in conjunction with Push_scope(), pop_scope(), and _local()
Push_scope() {
SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D=$(( $SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D + 1 ))
}
# Store the present value of the specified variable(s), allowing use in a new scope.
# To be used in conjunction with Push_scope(), pop_scope(), and _local()
#
# Parameters:
# $@ : string; name of variable to store the value of
scope_var() {
for varname_FB94CFD263CF11E89500036F7F345232 in "${@}"; do
eval "active_varnames_FB94CFD263CF11E89500036F7F345232=\"\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARNAMES}\""
# echo "Active varnames: ${active_varnames_FB94CFD263CF11E89500036F7F345232}"
case " ${active_varnames_FB94CFD263CF11E89500036F7F345232} " in
*" ${varname_FB94CFD263CF11E89500036F7F345232} "* )
# This variable was already stored in a previous call
# in the same scope. Do not store again.
# echo "Push \${varname_FB94CFD263CF11E89500036F7F345232}, but already stored."
:
;;
* )
if eval "[ -n \"\${${varname_FB94CFD263CF11E89500036F7F345232}+x}\" ]"; then
# Store the existing value from the previous scope.
# Only variables that were set (including set-but-empty) are stored
# echo "Pushing value of \$${varname_FB94CFD263CF11E89500036F7F345232}"
eval "SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_FB94CFD263CF11E89500036F7F345232}=\"\${${varname_FB94CFD263CF11E89500036F7F345232}}\""
else
# Variable is unset. Do not store the value; an unstored
# value will be used to indicate its unset state. The
# variable name will still be registered.
# echo "Not pushing value of \$${varname_FB94CFD263CF11E89500036F7F345232}; was previously unset."
:
fi
# Add to list of variables managed in this scope.
# List of variable names is space-delimited.
eval "SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARNAMES=\"\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARNAMES}${varname_FB94CFD263CF11E89500036F7F345232} \""
;;
esac
unset active_varnames_FB94CFD263CF11E89500036F7F345232
done
unset varname_FB94CFD263CF11E89500036F7F345232
}
# Simulate declaration of a local variable
# To be used in conjunction with Push_scope(), pop_scope(), and _local()
#
# This function is a convenience wrapper over scope_var().
#
# Can be called just like the local keyword.
# Example usage: _local foo="foofoofoo" bar="barbarbar" qux qaz=""
_local() {
for varcouple_44D4987063D111E8A46923403DDBE0C7 in "${@}"; do
# Example string: foo="barbarbar"
varname_44D4987063D111E8A46923403DDBE0C7="${varcouple_44D4987063D111E8A46923403DDBE0C7%%=*}"
varvalue_44D4987063D111E8A46923403DDBE0C7="${varcouple_44D4987063D111E8A46923403DDBE0C7#*=}"
varvalue_44D4987063D111E8A46923403DDBE0C7="${varvalue_44D4987063D111E8A46923403DDBE0C7#${varcouple_44D4987063D111E8A46923403DDBE0C7}}"
# Store the value for the previous scope.
scope_var "${varname_44D4987063D111E8A46923403DDBE0C7}"
# Set the value for this scope.
eval "${varname_44D4987063D111E8A46923403DDBE0C7}=\"\${varvalue_44D4987063D111E8A46923403DDBE0C7}\""
unset varname_44D4987063D111E8A46923403DDBE0C7
unset varvalue_44D4987063D111E8A46923403DDBE0C7
unset active_varnames_44D4987063D111E8A46923403DDBE0C7
done
unset varcouple_44D4987063D111E8A46923403DDBE0C7
}
# Simulate unsetting a local variable.
#
# This function is a convenience wrapper over scope_var().
#
# Can be called just like the unset keyword.
# Example usage: _unset foo bar qux
_unset() {
for varname_6E40DA2E63D211E88CE68BFA58FE2BCA in "${@}"; do
scope_var "${varname_6E40DA2E63D211E88CE68BFA58FE2BCA}"
unset "${varname_6E40DA2E63D211E88CE68BFA58FE2BCA}"
done
}
# Simulate exiting out of a nested variable scope
# To be used in conjunction with Push_scope(), pop_scope(), and _local()
pop_scope() {
eval "varnames_2581E94263D011E88919B3D175643B87=\"\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARNAMES}\""
# Cannot iterate over $varnames by setting $IFS; $IFS does not work
# properly on zsh. Workaround using string manipulation.
while [ -n "${varnames_2581E94263D011E88919B3D175643B87}" ]; do
# Strip enclosing spaces from $varnames.
while true; do
varnames_old_2581E94263D011E88919B3D175643B87="${varnames_2581E94263D011E88919B3D175643B87}"
varnames_2581E94263D011E88919B3D175643B87="${varnames_2581E94263D011E88919B3D175643B87# }"
varnames_2581E94263D011E88919B3D175643B87="${varnames_2581E94263D011E88919B3D175643B87% }"
if [ "${varnames_2581E94263D011E88919B3D175643B87}" = "${varnames_2581E94263D011E88919B3D175643B87}" ]; then
break
fi
done
# Extract the variable name for the current iteration and delete it from the queue.
varname_2581E94263D011E88919B3D175643B87="${varnames_2581E94263D011E88919B3D175643B87%% *}"
varnames_2581E94263D011E88919B3D175643B87="${varnames_2581E94263D011E88919B3D175643B87#${varname_2581E94263D011E88919B3D175643B87}}"
# echo "pop_scope() iteration on \$SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_2581E94263D011E88919B3D175643B87}"
# echo "varname: ${varname_2581E94263D011E88919B3D175643B87}"
if eval "[ -n \""\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_2581E94263D011E88919B3D175643B87}+x}"\" ]"; then
# echo "Value found. Restoring value from previous scope."
# echo eval "${varname_2581E94263D011E88919B3D175643B87}=\"\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_2581E94263D011E88919B3D175643B87}}\""
eval "${varname_2581E94263D011E88919B3D175643B87}=\"\${SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_2581E94263D011E88919B3D175643B87}}\""
unset "SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARVALUE_${varname_2581E94263D011E88919B3D175643B87}"
else
# echo "Unsetting \$${varname_2581E94263D011E88919B3D175643B87}"
unset "${varname_2581E94263D011E88919B3D175643B87}"
fi
# Variable cleanup.
unset varnames_old_2581E94263D011E88919B3D175643B87
done
unset SCOPE${SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D}_VARNAMES
unset varname_2581E94263D011E88919B3D175643B87
unset varnames_2581E94263D011E88919B3D175643B87
SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D=$(( $SCOPENUM_CEDD88E463CF11E8A72A3F9E5F08767D - 1 ))
}
スコープを有効にする関数は次のとおりです。
scope() {
eval "$(set)" command eval '\"\$@\"'
}
スクリプトの例:
x() {
y='in x'
echo "$y"
}
y='outside x'
echo "$y"
scope x
echo "$y"
結果:
outside x
in x
outside x