在bash脚本中导出一个数组
在bash脚本中导出一个数组
我无法像这样从一个bash脚本导出一个数组到另一个bash脚本:
export myArray[0]="Hello" export myArray[1]="World"
当我像这样写时,没有问题:
export myArray=("Hello" "World")
出于几个原因,我需要将我的数组初始化为多行。你有解决办法吗?
TL;DR:直到 bash-5.1(包括5.1),不直接支持可导出数组,但您可以通过以下两种方式之一有效地导出数组:
简单修改子脚本调用方式
使用导出函数存储数组初始化,并对子脚本进行简单修改
或者,您可以等到bash-4.3发布(截至2014年2月处于开发/RC状态,请参见Changelog中的ARRAY_EXPORT)。更新:此功能在4.3中未启用。如果在构建时定义了ARRAY_EXPORT,构建将失败。作者已经声明不打算完成此功能。
第一件要理解的是,bash环境(更正式地称为命令执行环境)与POSIX环境的概念不同。 POSIX环境是一个未类型化的name=value对的集合,并且可以以各种方式将其从一个进程传递到其子进程(有效地是IPC的有限形式)。
bash执行环境实际上是该集合的超集,具有类型化变量,只读和可导出标志,数组,函数等。这在一定程度上解释了set(bash内置)和env或printenv的输出不同之处。
当您调用另一个bash shell时,您启动了一个新进程,您会失去一些bash状态。但是,如果您以点形式源脚本,则脚本在同一环境中运行。或者,如果您通过()运行子shell,则环境也将得到保留(因为bash分叉,保留其完整状态,而不是使用进程环境重新初始化)。
@lesmana的答案中引用的限制是由POSIX环境简单地由name=value对组成并且没有额外意义而产生的,因此没有商定的方式来编码或格式化类型化变量,请参见下文有关函数的有趣bash怪癖以及bash-4.3中即将到来的变化(已放弃)提议的阵列特征。
使用内置的declare -p将bash环境中的某些部分作为一组或多组声明语句输出,以可重构“名称”的类型和值。这是基本序列化,但是与其他答案所暗示的复杂性相比相对较少。 declare -p保留数组索引,稀疏数组和有问题的值的引号。对于简单序列化数组,您可以只按行转储值,并使用read -a myarray还原它(对于连续的0索引数组有效,因为read -a自动分配索引)。
这些方法不需要对传递数组的脚本进行任何修改。
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file bash -c ". .bash_arrays; . otherscript.sh" # source both in the same environment
类似于上面的 bash -c "..."
形式有时被错误地用于 crontab 中设置变量。
替代方法包括:
declare -p array1 array2 > .bash_arrays # serialise to an intermediate file BASH_ENV=.bash_arrays otherscript.sh # non-interactive startup script
或者,作为单行命令:
BASH_ENV=<(declare -p array1 array2) otherscript.sh
最后一种方法使用进程替换来将declare
命令的输出作为 rc 脚本传递。(此方法仅适用于 bash-4.0 及更高版本:早期版本无条件地fstat()
rc 文件并使用返回的大小一次性read()
整个文件;FIFO 返回大小为 0,因此无法如预期地工作。)
在非交互 shell(即 shell 脚本)中,由BASH_ENV
变量指向的文件会被自动加载。您必须确保正确调用 bash,可能需要使用 shebang 明确调用 "bash",而不是 #!/bin/sh
,因为当 bash 处于历史/POSIX 模式下时,bash 不会遵守 BASH_ENV
。
如果您的所有数组名称恰好具有共同的前缀,则可以使用declare -p ${!myprefix*}
来扩展它们的列表,而不是枚举它们。
您可能不应尝试使用此方法导出并重新导入整个 bash 环境,因为一些特殊的 bash 变量和数组是只读的,并且在修改特殊变量时可能会有其他副作用。
(您还可以通过将数组定义序列化为可导出的变量,然后使用eval
来执行一些稍微不太愉快的事情,但我们不鼓励使用eval
...
$ array=([1]=a [10]="b c") $ export scalar_array=$(declare -p array) $ bash # start a new shell $ eval $scalar_array $ declare -p array declare -a array='([1]="a" [10]="b c")'
)
如上所述,存在一个有趣的问题:通过环境导出函数的特殊支持:
function myfoo() { echo foo }
使用export -f
或set +a
启用此行为,将导致进程环境中出现如下情况,可使用printenv
查看:
myfoo=() { echo foo }
变量是functionname
(或functioname()
)其值为() { functionbody }
。
当随后的 bash 进程启动时,它将从每个这样的环境变量中重新创建一个函数。如果您查看 bash-4.2 源文件variables.c
,您将看到以() {
开头的变量被特殊处理。(尽管使用此语法使用declare -f
创建函数是被禁止的。)更新:与此功能相关的 "shellshock" 安全问题,现代系统可能会禁用从环境中自动导入函数作为一种减轻措施。
如果您继续阅读,您将看到一条注释,其中#if 0
(或#if ARRAY_EXPORT
)保护检查以 ([
开头,以)
结尾的变量的代码,并指出"数组变量可能尚未导出"。 好消息是,在当前开发版本 bash-4.3rc2 中,启用了导出索引数组(非关联数组)的能力。上面已经指出,此功能不太可能启用。
我们可以使用这个来创建一个函数,以恢复所需的任何数组数据:
% function sharearray() { array1=(a b c d) } % export -f sharearray % bash -c 'sharearray; echo ${array1[*]}'
因此,类似于以前的方法,使用以下代码调用子脚本:
bash -c "sharearray; . otherscript.sh"
或者,您可以在适当的位置添加以下代码,有条件地在子脚本中调用sharearray
函数:
declare -F sharearray >/dev/null && sharearray
请注意,在sharearray
函数中没有declare -a
, 如果这样做,那么数组会被隐式地局部化到函数中,而不是期望的结果。 bash-4.2支持declare -g
,将函数声明的变量转换为全局变量,然后可以使用declare -ga
。 (由于关联数组需要declare -A
,因此在bash-4.2之前,您将无法使用此方法创建全局关联数组,从v4.2开始,declare -Ag
将像希望的那样工作。)GNU parallel
文档对此方法有有用的变体,请参阅man page中有关--env
的讨论。
从您的问题中得出,您可能还遇到了export
本身的问题。您可以在创建或修改名称后导出名称。 “exportable”是变量的标志或属性,为方便起见,您也可以在单个语句中设置和导出变量。直到bash-4.2,export
仅支持名称,支持简单(标量)变量或函数名称。
即使您将来可以导出数组,也可能不支持导出选择的索引(片段)(尽管由于数组是稀疏的,没有理由不允许这样做)。虽然bash还支持declare -a name [0]
语法,但下标会被忽略,“name”只是一个普通的索引数组。
数组变量目前还不能被导出。
来自ubuntu 10.04下bash版本4.1.5的人页。
以下声明来自Chet Ramey(截至2011年的当前bash维护者),可能是关于这个“bug”最官方的文档:
没有一种真正好的方法将数组变量编码到环境中。