Joh*_*ohn 42 variables shell posix scope function
是否有POSIX兼容方法将变量的范围限制为声明的函数?即:
Testing()
{
TEST="testing"
}
Testing
echo "Test is: $TEST"
Run Code Online (Sandbox Code Playgroud)
应打印"测试是:".我已经阅读过declare,local和typeset关键字,但它们看起来并不像POSIX内置函数.
Tay*_*nUB 56
它通常使用local关键字来完成,正如您所知,这不是由POSIX定义的.以下是关于向POSIX添加"本地"的信息性讨论.
然而,即使是我所知道的最原始的POSIX兼容shell,它也被某些GNU/Linux发行版/bin/sh默认使用dash(Debian Almquist Shell),它支持它.FreeBSD和NetBSD使用ash原始的Almquist Shell,它也支持它.OpenBSD使用的ksh实现/bin/sh也支持它.因此,除非您的目标是支持非GNU非BSD系统(如Solaris)或使用标准ksh等的系统,否则您可以放弃使用local.(可能想在脚本的开头放一些注释,在shebang行下面,注意它不是严格的POSIX sh脚本.只是为了不是邪恶.)说完这一切之后,你可能想检查一下sh支持所有这些实现的man-pages local,因为它们可能在它们的工作方式上有细微差别.或者只是不要使用local:
如果你真的想要完全符合POSIX,或者不想弄乱可能的问题,从而不使用local,那么你有几个选择.Lars Brinkhoff给出的答案是合理的,你可以将函数包装在子shell中.但是,这可能会产生其他不良影响.顺便说一下shell语法(每个POSIX)允许以下内容:
my_function()
(
# Already in a sub-shell here,
# I'm using ( and ) for the function's body and not { and }.
)
Run Code Online (Sandbox Code Playgroud)
虽然可能避免超级便携,但一些旧的Bourne shell甚至可以兼容非POSIX.只是想提一下POSIX允许它.
另一种选择是unset在函数体末端的变量,但是当然不会恢复旧的值,所以实际上并不是你想要的,它只会阻止变量的函数值泄漏到外面.我觉得不是很有用.
我能想到的最后一个也是疯狂的想法就是实现local自己.外壳虽然eval是邪恶的,却会产生一些疯狂的可能性.以下基本上实现了一个旧的Lisps动态范围,我将使用关键字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
Run Code Online (Sandbox Code Playgroud)
现在你可以:
$ 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
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,unlet可以同时给出任意数量的变量(作为不同的参数),为方便起见,上面没有展示.)
不要在家里试试这个,不要把它展示给孩子,不要把它展示给你的同事,不要把它展示给#bashFreenode,不要把它展示给POSIX委员会成员,不要把它展示给伯恩先生,或许可以向父亲麦卡锡的鬼魂展示给他笑.你被警告了,你没有向我学习.
编辑:
显然我已被殴打,greybot在Freenode上发送IRC机器人(属于#bash)命令"posixlocal"将使它给出一个模糊的代码,演示了一种在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
}
Run Code Online (Sandbox Code Playgroud)
此成绩单演示了用法:
$ x=a
$ y=b
$ f
test1 test2
$ echo $x $y
a b
Run Code Online (Sandbox Code Playgroud)
因此,它允许人们使用变量x和if表单y的then分支中的本地人.可以在else分支机构添加更多变量; 请注意,必须将它们添加两次,就像variable=在初始列表中一样,并且一次作为参数传递给typeset.请注意,不需要unlet左右(这是一个"透明"实现),并且没有完成名称修改和过多eval操作.所以它似乎是一个更清洁的整体实施.
编辑2:
出来typeset不是由POSIX定义,Almquist壳牌(FreeBSD中,NetBSD的Debian的)的实现不支持它.所以上面的hack将无法在这些平台上运行.
我相信最接近的是将函数体放在子shell中.
试试这个
foo()
{
( x=43 ; echo $x )
}
x=42
echo $x
foo
echo $x
Run Code Online (Sandbox Code Playgroud)
这实际上内置于 POSIX 函数声明的设计中。
如果您希望在函数中可以访问在父作用域中声明的 变量,但使其在父作用域中的值保持不变,只需:
*使用显式子 shell 声明您的函数,即使用
subshell_function() (with parentheses),不是inline_function() { with braces ;}内联分组与子 shell 分组的行为在整个语言中是一致的。
如果您想“混合和匹配”,请从内联函数开始,然后根据需要嵌套子 shell 函数。它很笨重,但很有效。