是否可以从函数中的单个语句声明并导出bash数组?
我目前的解决方法是先声明,然后再导出。
f() { foo=(1 2 3); export foo; }; f; export -p | grep foo=
declare -ax foo='([0]="1" [1]="2" [2]="3")'
我观察到:
f() { export bar=(1 2 3); }; f; export -p | grep bar=
<no output>
和
f() { export baz="(1 2 3)"; }; f; export -p | grep baz=
declare -x baz="(1 2 3)" # not an array
我使用bash v3.2.48(1) - 发布,无法升级。
一些背景知识:
我有一个朋友,我正在尝试研究“某些”Django。
他在命令行上比我更无能,并且在OSX hackintosh上需要以下内容:
在Windows上,我会为命令快捷方式添加别名,例如cmd.exe,设置自定义环境变量并启动ipython。这样做:在退出ipython之后,仍然可以在命令解释器中找到自己。
OSX的标准bash是否可以实现这一点?我玩过bash -c,但是很多东西都不起作用,比如改成目录,能够退出python并在终端中保持等等。也用-s玩。
答案 0 :(得分:5)
您无法在Bash(或任何其他shell)中导出数组。 Bash将从不将数组导出到环境中(直到某天可能实现,请参阅联机帮助页中的bugs
部分。)
这里有几件事情要发生。同样,在数组上设置-x
属性是没有意义的,因为它实际上不会被导出。您看到这些结果的唯一原因是因为您在对数组进行本地化之前定义了数组,导致数组下拉到该名称已在本地(或全局范围)生成的下一个最外层作用域)。
所以要明确的是,每当你使用声明命令时,你总是创建一个本地,除非你使用export
或readonly
非数组赋值作为参数,因为这些是POSIX,它没有指定本地或数组。
function f {
typeset -a a # "a" is now local to "f"
g
printf 'Now back in "f": %s\n' "$(typeset -p a)"
}
function g {
a=(1 2 3) # Assigning f's localized "a"
typeset -a a # A new local a
printf 'In "g": %s\n' "$(typeset -p a)" # g's local is now empty.
a=(a b c) # Now set g's local to a new value.
printf 'Still in "g": %s\n' "$(typeset -p a)"
}
f
# In "g": declare -a a='()'
# Still in "g": declare -a a='([0]="a" [1]="b" [2]="c")'
# Now back in "f": declare -a a='([0]="1" [1]="2" [2]="3")'
看来如果你给一个数组作为export
的参数,那么Bash会像任何其他声明命令一样使它成为局部的。基本上不要使用export
来定义数组。 export
是一个内置的POSIX,其行为未定义为数组,这可能就是为什么bash只是将它视为使用typeset -ax a=(1 2 3)
。使用typeset
,local
或declare
。真的没有办法知道什么是“正确的”,甚至与其他炮弹相比。 ksh93是唯一一个接受数组赋值作为声明命令参数的人,它的行为与Bash不一致。
要理解的重要一点是环境与它无关,当你尝试用POSIX-only命令做非标准的事情时,你只是在玩当地人的怪癖。
为了实际获得您想要的效果,您可以使用typeset -p
。
function f {
typeset -a "${1}=(1 2 3)"
typeset -p "$1"
}
typeset -a arr
eval "$(f arr)"
typeset -p arr
Bash保证你会得到正确的结果,但我发现这不是很有用,很少使用这种方法。通常最好让调用者定义一个本地并使用动态范围来完成工作(正如您已经发现的那样......只是在不导出的情况下进行)。
在Bash 4.3中,您可以使用typeset -n
,这是处理此问题的最正确方法。
答案 1 :(得分:3)
第一个选项似乎是唯一可行的选项。我试验了bash
4.2以及3.2.48。对我来说,有趣的信息是你的例子中的这些小变体:
$ f() { declare -a bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { export bar=(1 2 3); export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ f() { bar=(1 2 3); export bar; export -p | grep bar=; }; f; export -p | grep bar=
declare -ax bar='([0]="1" [1]="2" [2]="3")'
declare -ax bar='([0]="1" [1]="2" [2]="3")'
$ unset bar
$ f() { bar=(1 2 3); }; f; set | grep bar=
bar=([0]="1" [1]="2" [2]="3")
bar=(1 2 3)
$
在这些示例中,我测试函数内部的export
以及函数外部。因为变量是在函数中定义的,所以它们似乎是对函数的范围限制。例外情况是在应用任何属性之前定义变量 - 最后两个函数。创建一个全局变量,然后导出(在一种情况下)。
因此,如果您要从函数中导出数组,则必须在没有declare
或export
语句的情况下创建它(因为那些使变量本地化为函数),然后导出它。
我希望能够解释它;我可以模糊地看到发生了什么,并且在时尚之后它是有道理的。我不确定我是否应该解释它。
在bash
手册的declare
部分,它说:
在函数中使用时,
declare
使每个名称都是本地名称,与local
命令一样。
export
中没有相同的措辞。但是,观察到的行为好像 export
由declare -x
实现。
答案 2 :(得分:0)
如果我想导出数组MY_ARRAY,我使用:
[[ $MY_ARRAY ]] && export A_MY_ARRAY=$(declare -p MY_ARRAY)
在来电方和
[[ $A_MY_ARRAY =~ ^declare ]] && eval $A_MY_ARRAY
在子脚本。