提问人:terr 提问时间:10/6/2013 最后编辑:Ed Mortonterr 更新时间:10/31/2023 访问量:899
bash 和 ksh 中的间接引用
indirect references in bash and ksh
问:
我在 bash 和 ksh 中都有间接问题。下面的示例适用于 ksh。它使用 nameref(排版 -n),但它没有像我预期的那样工作。func_a将数组的名称传递给 func_b,以便对其进行修改(在这种简单情况下func_b向数组添加第二个条目)。这显然不起作用,因为func_b中定义的第二个本地变量发生了 与 nameref var2 引用的名称相同(func_a中的数组 var1)。 但是,拥有本机 nameref 类型(而不是在 bash 中使用的各种 eval hack)的原因之一不应该是不必处理这些动态范围问题,其中函数(在本例中为 func_b)按预期工作给某些调用者 函数,而不是仅仅因为局部定义的变量的名称而用于其他人? 似乎 nameref var 基本上只是一个别名或宏,而不是连接两个严格分离的范围的安全方法。我在 bash 上遇到了同样的问题,我希望 ksh 能够像 C 一样实现间接引用(好吧,出于安全原因,当然不像低级指针,但至少可以说具有类似的“范围隔离”)。我错过了什么吗?
func_b ()
{
typeset -n var2=$1
typeset -i var1
var2[1]=b
}
func_a ()
{
typeset -a var1=( a )
func_b var1
echo "${var1[@]}"
}
Ed Morton 编辑:我花了一段时间才弄清楚这个问题是关于什么的,所以这里是 - OP 关注这两个脚本的输出之间的差异,具体取决于是否在 :local var1
func_b()
脚本 1:
$ cat tst.sh
#!/usr/bin/env bash
func_b ()
{
local -n var2=$1
local var1
var2=99999
}
func_a ()
{
local var1=3
func_b var1
echo "NOTE still original contents: $var1"
}
func_a
$ ./tst.sh
NOTE still original contents: 3
脚本 2:
$ cat tst.sh
#!/usr/bin/env bash
func_b ()
{
local -n var2=$1
#local var1 # << Note: now commented out
var2=99999
}
func_a ()
{
local var1=3
func_b var1
echo "NOTE now modified contents: $var1"
}
func_a
$ ./tst.sh
NOTE now modified contents: 99999
答:
我找到了一个解决方案,好吧,这家伙 http://fvue.nl/wiki/Bash:_Passing_variables_by_reference 完全归功于他。我不喜欢他使用他关于 bash 的未设置的发现的方式 但任何人都可以以自己的方式使用此属性。所以这是它的要点:
上面的代码在 bash 中是这样的
func_b ()
{
local var2=$1
local -i var1
#do some work to compute the value b
#....
#....
#And in the end assign it with the indirect reference
eval "$var2[1]=b"
}
func_a ()
{
local -a var1=( a )
func_b var1
echo "${var1[@]}"
}
(可以避免使用评估,但让我们保持重点) 问题显然是 func_b 中的本地 var1 遮蔽了 func_a 中的 var1 由 var2 于 func_b 年引用。因此,func_b确实按预期运行,即第二个条目 在调用方的数组中通过间接引用添加,仅当调用方不添加时 将其数组命名为“var1”。比方说,在“做一些工作”部分之后,我知道我已经完成了 使用我在 func_b 中使用的局部变量 var1(可能用于计算想要的 值 b)。在这一点上,我可以做到这一点
func_b ()
{
local var2=$1
local -i var1
#do some work to compute the value b
#....
#....
#And in the end assign it with the indirect reference
unset var1
eval "$var2[1]=b"
}
去除func_a的 var1 上的“阴影”并正确终止计算。 但是 bash unset 不允许这样做。一旦我在 func_b 中设置了 loval var1,即使 在某些时候我取消设置它,它仍然会遮蔽func_a的 var1。 上面的家伙发现,unset 实际上可以通过调用堆栈到达 并取消设置func_b的 var1,但仅当从 func_b 调用上方的函数 F 调用时 在堆栈中,IF 函数 f 不会不在本地定义它自己的 var1。 基本上,如果你这样做
func_the_unshadower ()
{
unset -v var1
}
func_b ()
{
local var2=$1
local -i var1
#do some work to compute the value b
#....
#....
#And in the end assign it with the indirect reference
func_the_unshadower
eval "$var2[1]=b"
}
func_a ()
{
local -a var1=( a )
func_b var1
echo "${var1[@]}"
}
它有效.... 显然,这只是一个玩具示例,每个人都可以弄清楚自己的喜好 使用 unset 属性的方法。一个简单的方法是在运行时检查变量是否引用了 按名称被一些本地 var 通过调用不带参数的“local”(其中 返回本地变量的列表)。 但最棒的是,这不是 bash 中的错误。在上面的链接中,有 甚至是指向 bash 邮件列表中主要 bash 开发人员的线程的链接 说这是 Unset 的预期行为方式,并且会保持这种状态。
编辑 埃德·莫顿(Ed Morton):因此,鉴于上述情况,为了修复我在问题底部添加的示例,我们可以这样做:
$ cat tst.sh
#!/usr/bin/env bash
unset_vars() {
unset -v "$@"
}
func_b ()
{
local -n var2=$1
local var1
unset_vars var1
var2=99999
}
func_a ()
{
local var1=3
func_b var1
echo "NOTE now modified contents: $var1"
}
func_a
$ ./tst.sh
NOTE now modified contents: 99999
评论
我发现用以下语法声明函数:
function func_a
它有效。这是因为此 ksh93 语法使排版声明的变量成为局部变量,而使用原始语法时,POSIX 规则适用并且变量是全局的。
皮特
评论
local
var1
func_b()
var1
func_a()
func_b()
func_a()
func_b()
local var1