将符合POSIX的方法将变量范围应用于Shell脚本中的函数

前端之家收集整理的这篇文章主要介绍了将符合POSIX的方法将变量范围应用于Shell脚本中的函数前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
是否有符合POSIX的方法来将变量的范围限制为声明的函数?即:
Testing()
{
    TEST="testing"
}

Testing
echo "Test is: $TEST"

应该打印“测试是:”。我已经阅读了关于声明,本地和排版的关键字,但它看起来不像POSIX内置的。

通常使用本地关键字来完成,就像你似乎知道的那样,它不是由POSIX定义的。这是一个资料丰富的 discussion about adding ‘local’ to POSIX

然而,即使是最原始的符合POSIX的shell,我知道一些GNU / Linux发行版使用哪个作为/ bin / sh默认,dash(Debian Almquist Shell)支持它。 FreeBSD和NetBSD使用ash,原来的Almquist Shell也支持它。 OpenBSD使用/ bin / sh的ksh实现也支持它。所以除非你的目标是支持像Solaris这样的非GNU非BSD系统,还有那些使用标准的ksh等,那么你可以使用本地的方法来避免。 (可能想在脚本开始时放下一些评论,在shebang行之下,注意到它不是一个严格的POSIX sh脚本,只是不是邪恶的)所有这一切,你可能想检查相应的所有这些支持本地的sh实现的手册页,因为它们的工作方式可能有微妙的差异。或者不要使用本地:

如果你真的想要完全符合POSIX,或者不想混淆可能的问题,因此不使用本地,那么你有几个选项。 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 }.
)

虽然可能避免超级便携式,但一些旧的Bourne外壳甚至可以与POSIX兼容。只是想提到POSIX允许它。

另一个选择是在函数体的末尾取消设置变量,但是当然不会恢复旧值,所以不是真的想要的,我猜这只会阻止变量的函数内值泄漏。不是很有用我猜

我可以想到的最后一个,疯狂的想法是实现本地自己。 shell具有eval,但是它是邪恶的,可以产生一些疯狂的可能性。以下基本上实现了一个旧的Lisps的动态范围,我将使用关键字let而不是本地进一步的酷点,尽管你必须使用所谓的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委员会的成员,不应该告诉Bourne先生,也许把它展示给麦卡锡的父亲给他一个笑。你被警告过,你没有从我那里学习。

编辑:

显然我被殴打,在Freenode(属于#bash)上发送IRC bot greybot命令“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
}

本记录表明用法

$ x=a
$ y=b
$ f
test1 test2
$ echo $x $y
a b

所以它可以使用变量x和y作为if表单的then分支中的本地化。更多变量可以添加到else分支;请注意,必须将它们两次添加,一次与初始列表中的variable =一样,并作为参数传递给排版。请注意,不需要等等(这是一个“透明”的实现),并且没有名称变化和过多的eval完成。因此,整体来说,这似乎是一个更清洁的实现。

编辑2:

出版排版不是由POSIX定义的,Almquist Shell(FreeBSD,NetBSD,Debian)的实现不支持它。所以上述黑客在这些平台上不行。

猜你在找的Bash相关文章