使用ksh中的'typeset -r'和'readonly'关键字确定异常

时间:2013-04-01 22:46:22

标签: bash shell ksh readonly typeset

首先,我知道使用function关键字与myfunction()声明函数时bash和ksh之间的一般范围差异(动态/静态),此帖仅讨论了范围问题只读变量。

今天我偶然发现了令我困惑的事情。我有一个脚本从使用function关键字声明的函数中获取文件(因此我没有立即知道为什么会发生以下情况,因为我通过&#34查看了这些单独的文件; global-范围护目镜")。  作为最近清理的一部分,我在这些源文件中只读取了各种变量,并注意到代码的某些部分在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

观察:

  • readonlytypeset -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只在本地和全局范围内都是只读的,但这是预期的,因为它将全局变量标记为只读,并且由于动态范围,它在函数中也变为只读(旁注:see also )。
      • [5] quux=function; typeset -r quux'function'分配给全局变量quux,然后声明一个没有值的新局部变量quux
    • readonly从不声明新的(本地)变量(如预期的那样)。对于[1]和[4],它只读取全局变量,对于[3]新的局部变量。这完全是我所期望的,意味着readonly操作的范围与相应变量本身的范围相同,即

      • x=y;如果$x是本地的,则readonly x会标记本地变量x readonly
      • x=y;如果$x是全局的,那么readonly x会标记全局变量x readonly
    • 分别为[1] / [2]和[4] / [5]之间的一致性

  • 在ksh中
  • (讨论f; g的行为与预期一致):

    • 还有3种可能的不同场景:
      • [6] readonly foo=function:类似于[5],[8]和[9],但更令人困惑,因为这是一个命令(相反于:赋值优先,readonly / { {1}}之后)。显然,typeset -r声明了一个没有值的新局部变量readonly,但将全局变量foo设置为foo?!'function'在功能和全局范围内都是只读的。
      • [7] footypeset -r bar=function分配给新的本地变量'function'(与[8]相同)
      • [8] bartypeset baz=function; readonly baz分配给新的本地变量'function'(与[7]相同)。 baz仅在函数范围内变为只读
      • [9] bazqux=function; readonly qux分配给全局变量'function',然后声明一个没有值的新局部变量qux(与[5],[6]相同) ,[10])。 qux仅在函数范围内变为只读
      • [10] quxquux=function; typeset -r quux分配给全局变量'function',然后声明一个没有值的新局部变量quux(与[5],[9]相同) ,[10])。 quux仅在函数范围内变为只读。
    • quux似乎确实在[6]和[9]中声明了新的局部变量,但在[8]中却没有。

预计bash的行为。 readonly(= typeset)在函数范围内创建/修改(bash具有declare选项以强制在全局范围内创建/修改,即使在函数内部使用时),{{1真的只有"标记"现有变量,永远不会引入新的局部变量。 [5]我有点困惑,因为我以前从来没有必要声明一个未设置的只读变量,我会假设如果存在一个同名的全局变量,它会修改它,但我绝对可以忍受这种行为,因为它是一致的与其他场景和-g的存在。

但是,根据我的理解,ksh手册页未能完全解释上述所有场景以及readonly-g关键字之间的细微差别,除非我在重读时忽略了某些内容问题各节。 对我来说最让人困惑的是,它在readonlytypeset -r之间的范围差异解释附近提到关键字readonly,以及foo()的简短说明inbuilt也没有提到这一点。基于此,我永远不会假设function bar引入了新的,静态范围的变量,如readonly,并且在某些(但不是所有)场景中它确实出乎意料。

对我来说最令人困惑的情况是[6]并且我无法理解那里到底发生了什么(这也是破坏我的代码的具体方案)。

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

2 个答案:

答案 0 :(得分:1)

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

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

之间的区别

功能blah {}

blah(){}

看广告人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.

答案 1 :(得分: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