1

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没有值的新局部变量
    • readonly从不声明新的(局部)变量(如预期的那样)。对于 [1] 和 [4],它将全局变量标记为只读,对于 [3],它标记新的局部变量。这完全是我所期望的,这意味着readonly运行的范围与相应变量本身的范围相同,即

      • x=y; if $xis 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仅在函数范围内变为只读。
    • readonly似乎在 [6] 和 [9] 中声明了新的局部变量,但在 [8] 中没有。

bash 的行为是预期的。typeset(= declare) 在函数范围内创建/修改(bash 可以-g选择强制在全局范围内创建/修改,即使在函数内部使用),readonly实际上只是“标记”现有变量,从不引入新的局部变量。[5] 让我有点困惑,因为我之前从未声明过未设置的只读变量,我会假设如果存在同名的全局变量,它会修改它,但我绝对可以忍受这种行为,因为它是一致的与其他场景和存在-g

但是,据我了解,ksh 手册页无法完全解释上述所有场景以及关键字readonlytypeset -r关键字之间的细微差别,除非我在重新阅读问题中的相应部分时遗漏了某些内容。最让我困惑的是,它在 and 之间的作用域差异的解释附近没有提到关键字,readonly并且内置的简短解释也没有提到任何关于此的内容。基于此,我永远不会假设像 do 那样引入新的、静态范围的变量,而且在某些(但不是全部)场景中这样做似乎真的出乎意料。foo()function barreadonlyreadonlytypeset -r

对我来说最令人困惑的场景是 [6],我无法理解那里到底发生了什么(这也是破坏我的代码的特定场景)。

我的观察是否正确,有人可以阐明 ksh93 的行为吗?(我希望这个问题的范围可以接受(没有双关语))

4

2 回答 2

1

欢迎来到 shell 不兼容的世界 :)

如果我正确理解了这个问题,那就是关于

功能废话{}

废话(){}

寻找广告人 ksh(1)(在 Solaris 上,如果这有什么不同的话)

 Functions defined by the function name syntax and called  by
 name execute in the same process as the caller and share all
 files and present working directory with the  caller.
 ...
 Ordinarily,  variables
 are  shared  between  the  calling program and the function.
 However, the typeset special built-in command used within  a
 function  defines  local  variables whose scope includes the
 current function. They can be passed to functions that  they

相对

 Functions defined  with  the  name()  syntax  and  functions
 defined  with the function name syntax that are invoked with
 the . special built-in are executed in the caller's environ-
 ment  and  share  all  variables  and traps with the caller.
 Errors within these function  executions  cause  the  script
 that contains them to abort.
于 2013-05-06T13:52:40.597 回答
1

错误如下,当您不使用"local"或使函数成为子shell时,函数没有“本地”范围!

因此,使用f() ( <code> )而不是f() { <code> }获取您的本地范围!

不过,你有一点!

"readonly <var>"和的区别"declare -r <var>"

# ./readonly_test.sh
bash: 3.00.16(1)-release

inside function
----------------
foo=function
bar=function
baz=function
qux=function
quux=

global scope after calling
----------------------------
foo=global
bar=global
baz=global
qux=global
quux=global
于 2013-08-22T12:55:25.560 回答