在bash脚本中导出一个数组

46 浏览
0 Comments

在bash脚本中导出一个数组

我无法像这样从一个bash脚本导出一个数组到另一个bash脚本:

export myArray[0]="Hello"
export myArray[1]="World"

当我像这样写时,没有问题:

export myArray=("Hello" "World")

出于几个原因,我需要将我的数组初始化为多行。你有解决办法吗?

admin 更改状态以发布 2023年5月23日
0
0 Comments

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 -fset +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”只是一个普通的索引数组。

0
0 Comments

数组变量目前还不能被导出。

来自ubuntu 10.04下bash版本4.1.5的人页。

以下声明来自Chet Ramey(截至2011年的当前bash维护者),可能是关于这个“bug”最官方的文档:

没有一种真正好的方法将数组变量编码到环境中。

http://www.mail-archive.com/bug-bash@gnu.org/msg01774.html

0