function
首先,在使用关键字 vs.声明函数时,我知道 bash 和 ksh 之间的一般范围界定差异(动态/静态),myfunction()
并且这篇文章仅讨论有关只读变量的范围界定问题。
不过,今天我偶然发现了一些让我感到困惑的事情。我有一个脚本,它从使用关键字声明的函数中获取文件function
(因此,当我通过“全局范围护目镜”查看这些单独的文件时,我并没有立即意识到为什么会发生以下情况)。作为最近清理的一部分,我将这些源文件中的各种变量设置为只读,并注意到代码的某些部分在 ksh93 下停止工作,这取决于我如何将变量标记为只读。更具体地说,如果我使用readonly FOO=bar
,${FOO}
将对源文件的其余部分取消设置。
这说明了问题:
(注意:内联代码的行为是相同的(与获取源代码的第二个脚本相比),但由于它在这里保存了一些行并且帖子已经很长了,所以我保持原样)
readonly_test_sourced.sh:
readonly foo=function
typeset -r bar=function
typeset baz=function
readonly baz
qux=function
readonly qux
quux=function
typeset -r quux
readonly_test.sh:
function f
{
. ./readonly_test_sourced.sh
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
}
g()
{
. ./readonly_test_sourced.sh
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
}
[ -n "${KSH_VERSION}" ] && echo "ksh: ${KSH_VERSION}"
[ -n "${BASH_VERSION}" ] && echo "bash: ${BASH_VERSION}"
for var in foo bar baz qux quux; do
unset "${var}"
eval "$var=global" # don't do this at home, there are better ways
done
func="${1:-f}"
echo
echo "inside function ${func}"
echo '----------------'
${func}
echo
echo "global scope after calling ${func}"
echo '----------------------------'
printf "foo=${foo}\nbar=${bar}\nbaz=${baz}\nqux=${qux}\nquux=${quux}\n"
这是我使用 ksh93u+ 得到的输出:
$ ksh ./readonly_test.sh f
ksh: Version JM 93u+ 2012-02-29
inside function f
----------------
foo=
bar=function
baz=function
qux=
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ ksh ./readonly_test.sh g
ksh: Version JM 93u+ 2012-02-29
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=function
global scope after calling g
----------------------------
foo=function
bar=function
baz=function
qux=function
quux=function
这就是我在 bash 4.2 中得到的:
$ bash ./readonly_test.sh f
bash: 4.2.42(1)-release
inside function f
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ bash ./readonly_test.sh g
bash: 4.2.42(1)-release
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling g
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
我还通过 mksh 运行它(它根据其手册页实现动态范围,它确实产生与使用 bash 相同的结果):
$ mksh ./readonly_test.sh f
ksh: @(#)MIRBSD KSH R40 2012/03/20
inside function f
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling f
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
$ mksh ./readonly_test.sh g
ksh: @(#)MIRBSD KSH R40 2012/03/20
inside function g
----------------
foo=function
bar=function
baz=function
qux=function
quux=
global scope after calling g
----------------------------
foo=function
bar=global
baz=global
qux=function
quux=function
观察:
readonly
有时typeset -r
是同义词,有时不是bash 和 mksh
有 3 种可能的不同情况(对于 f 和 g):
- [1]
readonly foo=function
赋值'function'
给全局变量foo
,不声明新的局部变量(与 [4] 相同) - [2]
typeset -r bar=function
分配'function'
给新的局部变量bar
(与 [3] 相同) - [3]
typeset baz=function; readonly baz
分配'function'
给新的局部变量baz
(与 [2] 相同) - [4]
qux=function; readonly qux
分配'function'
给全局变量qux
并且不声明新的局部变量(与 [1] 相同)。qux
在本地和全局范围内都是只读的,但这是可以预期的,因为它将全局变量标记为只读,并且由于动态范围,它在函数中也变为只读(旁注:另请参见)。 - [5]
quux=function; typeset -r quux
赋值'function'
给全局变量quux
,然后声明一个quux
没有值的新局部变量
- [1]
readonly
从不声明新的(局部)变量(如预期的那样)。对于 [1] 和 [4],它将全局变量标记为只读,对于 [3],它标记新的局部变量。这完全是我所期望的,这意味着readonly
运行的范围与相应变量本身的范围相同,即x=y
; if$x
is local thenreadonly x
会将局部变量标记为x
只读x=y
; 如果$x
是全局的,那么readonly x
会将全局变量标记为x
只读
[1] / [2] 和 [4] / [5] 之间的一致性,分别
在 ksh 中(讨论 f;g 的行为符合预期):
- 还有3种可能的不同情况:
- [6]
readonly foo=function
:类似于 [5]、[8] 和 [9],但更令人困惑,因为这是一个单一的命令(相对于:先赋值,readonly
后赋值typeset -r
)。显然readonly
声明了一个foo
没有值的新局部变量,但将全局变量设置foo
为'function'
. ?!?.foo
在函数和全局范围内都变为只读。 - [7]
typeset -r bar=function
分配'function'
给新的局部变量bar
(与 [8] 相同) - [8]
typeset baz=function; readonly baz
分配'function'
给新的局部变量baz
(与 [7] 相同)。baz
仅在函数范围内变为只读 - [9]
qux=function; readonly qux
分配'function'
给全局变量qux
,然后声明一个qux
没有值的新局部变量(与 [5]、[6]、[10] 相同)。qux
仅在函数范围内变为只读 - [10]
quux=function; typeset -r quux
分配'function'
给全局变量quux
,然后声明一个quux
没有值的新局部变量(与 [5]、[9]、[10] 相同)。quux
仅在函数范围内变为只读。
- [6]
readonly
似乎在 [6] 和 [9] 中声明了新的局部变量,但在 [8] 中没有。
- 还有3种可能的不同情况:
bash 的行为是预期的。typeset
(= declare
) 在函数范围内创建/修改(bash 可以-g
选择强制在全局范围内创建/修改,即使在函数内部使用),readonly
实际上只是“标记”现有变量,从不引入新的局部变量。[5] 让我有点困惑,因为我之前从未声明过未设置的只读变量,我会假设如果存在同名的全局变量,它会修改它,但我绝对可以忍受这种行为,因为它是一致的与其他场景和存在-g
。
但是,据我了解,ksh 手册页无法完全解释上述所有场景以及关键字readonly
和typeset -r
关键字之间的细微差别,除非我在重新阅读问题中的相应部分时遗漏了某些内容。最让我困惑的是,它在 and 之间的作用域差异的解释附近没有提到关键字,readonly
并且内置的简短解释也没有提到任何关于此的内容。基于此,我永远不会假设像 do 那样引入新的、静态范围的变量,而且在某些(但不是全部)场景中这样做似乎真的出乎意料。foo()
function bar
readonly
readonly
typeset -r
对我来说最令人困惑的场景是 [6],我无法理解那里到底发生了什么(这也是破坏我的代码的特定场景)。
我的观察是否正确,有人可以阐明 ksh93 的行为吗?(我希望这个问题的范围可以接受(没有双关语))