全局关联数组仅在第二次获取脚本时可见

全局关联数组仅在第二次获取脚本时可见

我调用了以下脚本,.bash_functions.test该脚本已由我的.bash_functions脚本获取:

# vim: set syn=sh noet:

mp4Options_BIS="-movflags +frag_keyframe"
declare -A audioExtension=( [libspeex]=spx [speex]=spx [opus]=opus [vorbis]=ogg [aac]=m4a [mp3]=mp3 [mp2]=mp2 [ac3]=ac3 [wmav2]=wma [pcm_dvd]=wav [pcm_s16le]=wav )

function test1 {
    echo "=> mp4Options_BIS = $mp4Options_BIS"
    echo "=> audioExtension = ${audioExtension[*]}"
}

当我运行该test1函数时,我看到以下内容:

=> mp4Options_BIS = -movflags +frag_keyframe
=> audioExtension = 

最后,当我再次获取脚本并重新运行该test1函数时,我看到了以下内容:

=> mp4Options_BIS = -movflags +frag_keyframe
=> audioExtension = ac3 wma opus mp3 wav mp2 wav spx m4a spx ogg

事实上,我Source在第一个源调用以及source内置函数和第二个源调用中使用了我的函数:

$ grep -r .bash_functions.test 
.bash_functions:source $initDir/.bash_functions.test
$ type Source 
Source is a function
Source () 
{ 
    test "$debug" -gt 0 && time source "$@" && echo || source "$@"
}

发生的事情是这样的:

$ Source .initBash/.bash_functions.test
$ test1
=> mp4Options_BIS = -movflags +frag_keyframe
=> audioExtension = 
$ source .initBash/.bash_functions.test
$ test1
=> mp4Options_BIS = -movflags +frag_keyframe
=> audioExtension = ac3 wma opus mp3 wav mp2 wav spx m4a spx ogg

为什么它会这样工作?

答案1

declare/ typesetwithout-g除了设置类型之外还声明当前作用域中的变量。

在这里,因为declare -A audioExtension=(...)最终在Source函数内运行,所以导致audioExtension变量被声明为该函数的本地变量,因此一旦Source返回,其定义就会丢失。

您可以将其更改为typeset -Ag audioExtension=(...)始终在全局范围内声明变量(它与 zsh/mksh/yash 不同,后者typeset -g仅阻止变量成为本地变量(仅更新类型/属性和值);当您的Source函数从另一个函数本身调用;参见bash 与 zsh:范围界定和 `typeset -g`了解详情)。

如果您使用ksh93而不是bash(这是 shellbash借用了关联数组语法),您可以将您的Source函数定义为:

Source() {
  ...
}

与以下相反:

function Source {
  ...
}

在 ksh93 中,使用 Bourne 风格func() cmd语法定义的函数没有本地作用域,而function func {具有本地作用域静止的本地范围(那里,只有全球范围和本地每个函数作用域,而不是本地作用域的堆栈)。

bash(以及其他具有本地作用域的 shell)中,没有类似的方法可以让函数不引入新的作用域。您可以使用alias类似的替代:

shopt -s expand_aliases
if [ "$debug" -gt 0 ]; then
  alias Source='time source'
else
  alias Source=source
fi

(请注意,别名在代码被扩展时被扩展,当时还没有被处决)。

与您的函数方法相比,它会在功能上产生差异:

Source ./myfile | other-cmd

我们将对两者进行计时source myfile,并且other-cmd随着它扩展到time source ./myfile | other-cmdwhile 在函数方法中,我们只会通过计时source ./myfile

使用source=(time source); "${source[@]}" /some/file不起作用(它将调用time独立实用程序而不是bashtime 关键字)。

相关内容