在bash脚本中导出数组

Rem*_*iii 49 arrays bash export

我无法将数据从bash脚本导出到另一个bash脚本,如下所示:

export myArray[0]="Hello"
export myArray[1]="World"
Run Code Online (Sandbox Code Playgroud)

当我这样写时没有问题:

export myArray=("Hello" "World")
Run Code Online (Sandbox Code Playgroud)

由于几个原因,我需要将我的数组初始化为多行.你有什么解决方案吗?

les*_*ana 48

可能尚未导出数组变量.

从ubuntu 10.04下的bash版本4.1.5的联机帮助页.

Chet Ramey(截至2011年的当前bash维护者)的以下声明可能是关于此"bug"的最官方文档:

将数组变量编码到环境中并不是一种好方法.

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

  • 呃......对于bash所做的所有事情,它无法导出数组供其他脚本使用...... (3认同)
  • 奇怪的是,[bash参考](http://www.gnu.org/software/bash/manual/bashref.html)没有提到这一点 (2认同)
  • 出于相同的原因,您不能导出数组,因为首先需要数组:您不能在不丢失数组结构的情况下将数组安全地展平为单个以空分隔的字符串。 (2认同)

mr.*_*tic 34

TL; DR:直接支持可导出的数组,包括bash-4.3,但您可以(有效地)以两种方式之一导出数组:

  • 对子脚本调用方式的简单修改
  • 使用导出的函数来存储数组初始化,只需对子脚本进行简单修改即可

或者,您可以等到bash-4.3发布(截至2014年2月处于开发/ RC状态,请参阅更改日志中的ARRAY_EXPORT).更新:4.3中启用此功能.如果定义ARRAY_EXPORT何时构建,则构建将失败.作者表示不打算完成此功能.


首先要理解的是,bash环境(更恰当的命令执行环境)与环境的POSIX概念不同.该POSIX环境是无类型的集合name=value对,并可以从它的孩子的过程来通过各种方式(有效限量表IPC).

bash执行环境实际上是这个的超集,具有类型变量,只读和可导出标志,数组,函数等.这部分解释了为什么set(bash builtin)的输出和/ envprintenv不同.

当你调用另一个bash shell时,你正在开始一个新的进程,你会失去一些bash状态.但是,如果您点源脚本,脚本将在同一环境中运行; 或者如果你通过( )环境运行子shell 也会被保留(因为bash forks,保留其完整状态,而不是使用进程环境重新初始化).


@ lesmana答案中引用的限制是因为POSIX环境只是name=value成对而没有额外的意义,所以没有商定的方式来编码或格式化类型变量,请参阅下面有关函数的有趣bash怪癖,以及即将发生的bash-4.3更改(建议放弃阵列功能).

有几种简单的方法可以使用declare -p(内置)将一些bash环境输出为一组一个或多个declare语句,可以用来重建"名称"的类型和值.这是基本的序列化,但其他一些答案的复杂性相当低.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
Run Code Online (Sandbox Code Playgroud)

bash -c "..."在crontabs中有时(错误地)使用上述形式的变体来设置变量.

替代方案包括:

declare -p array1 array2 > .bash_arrays       # serialise to an intermediate file
BASH_ENV=.bash_arrays otherscript.sh          # non-interactive startup script
Run Code Online (Sandbox Code Playgroud)

或者,作为一个单行:

BASH_ENV=<(declare -p array1 array2) otherscript.sh
Run Code Online (Sandbox Code Playgroud)

最后一个使用进程替换declare命令的输出作为rc脚本传递.(此方法仅适用于bash-4.0或更高版本:早期版本无条件fstat()rc文件并使用一次返回read()文件的大小; FIFO返回大小为0,因此无法按预期工作.)

非交互式shell(即shell脚本)中,BASH_ENV变量指向的文件是自动获取的.你必须确保正确调用bash,可能使用shebang显式调用"bash",而不是在历史/ POSIX模式下#!/bin/shbash不会被尊重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")'
Run Code Online (Sandbox Code Playgroud)

)


如上所述,有一个有趣的怪癖:通过环境导出函数的特殊支持:

function myfoo() {
    echo foo
}
Run Code Online (Sandbox Code Playgroud)

使用export -fset +a启用此行为,将在(进程)环境中生成此内容,可见printenv:

myfoo=() { echo foo
}
Run Code Online (Sandbox Code Playgroud)

变量是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[*]}'
Run Code Online (Sandbox Code Playgroud)

因此,与以前的方法类似,使用以下命令调用子脚本:

bash -c "sharearray; . otherscript.sh"
Run Code Online (Sandbox Code Playgroud)

或者,您可以sharearray通过在某个适当的位置添加来有条件地调用子脚本中的函数:

[ "`type -t sharearray`" = "function" ] && sharearray
Run Code Online (Sandbox Code Playgroud)

注意declare -asharearray函数中没有,如果你这样做,那么数组隐含在函数的本地,这不是你想要的.bash-4.2支持declare -g显式地使变量成为全局变量,因此declare -ga可以使用().(由于关联数组需要 declare -A你将无法使用这种方法用于关联数组之前与bash-4.2)GNU的parallel文档,对这种方法带来有益的变化,看到的讨论--env手册页.


你的问题也表明你可能遇到了问题export.您可以在创建或修改名称后导出名称."exportable"是变量的标志或属性,为方便起见,您还可以在单​​个语句中设置和导出.直到bash-4.2 export只需要一个名称,支持简单(标量)变量或函数名称.

即使您可以(将来)导出数组,也可能不支持导出所选索引(切片)(尽管由于数组稀疏,因此没有理由不允许它).虽然bash也支持语法declare -a name[0],但是下标被忽略,"name"只是一个普通的索引数组.


Mat*_*ock 8

天啊.我不知道为什么其他答案让这么复杂.Bash几乎内置了对此的支持.

在导出脚本中:

myArray=( '  foo"bar  ' $'\n''\nbaz)' )  # an array with two nasty elements

myArray="${myArray[@]@Q}" ./importing_script.sh
Run Code Online (Sandbox Code Playgroud)

(注意,双引号是正确处理数组元素中的空格所必需的.)

在进入时importing_script.sh,myArray环境变量的值包含这些精确的26个字节:

'  foo"bar  ' $'\n\\nbaz)'
Run Code Online (Sandbox Code Playgroud)

然后以下将重新构建数组:

eval "myArray=( ${myArray} )"
Run Code Online (Sandbox Code Playgroud)

警告!eval如果您不能信任myArray环境变量的来源,请不要这样.这个技巧展示了"Little Bobby Tables"漏洞.试想一下,如果有人要设置的值myArray) ; rm -rf / #.