在 bash 脚本中导出数组

发布于 2024-10-30 20:30:25 字数 239 浏览 2 评论 0 原文

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

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

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

export myArray=("Hello" "World")

由于多种原因,我需要将数组初始化为多行。你有什么解决办法吗?

I can not export an array from a bash script to another bash script like this:

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

When I write like this there are no problem:

export myArray=("Hello" "World")

For several reasons I need to initialize my array into multiple lines. Do you have any solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

宁愿没拥抱 2024-11-06 20:30:25

数组变量可能无法导出。

来自 ubuntu 10.04 下 bash 版本 4.1.5 的联机帮助页。

Chet Ramey(2011 年现任 bash 维护者)的以下声明可能是有关此“bug”的最官方文档:

实际上没有一种好的方法可以将数组变量编码到环境中。

http://www.mail-archive.com/[电子邮件受保护]/msg01774.html

Array variables may not (yet) be exported.

From the manpage of bash version 4.1.5 under ubuntu 10.04.

The following statement from Chet Ramey (current bash maintainer as of 2011) is probably the most official documentation about this "bug":

There isn't really a good way to encode an array variable into the environment.

http://www.mail-archive.com/[email protected]/msg01774.html

醉态萌生 2024-11-06 20:30:25

TL;DR:可导出数组在 bash-5.1 及之前版本均不直接受支持,但您可以通过以下两种方式之一(有效)导出数组:

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

或者,您可以等到bash-4.3发布(正在开发中/截至 2014 年 2 月的 RC 状态,请参阅变更日志中的 ARRAY_EXPORT)。 更新:此功能在 4.3 中启用。如果在构建时定义了 ARRAY_EXPORT ,构建将会失败。作者已声明不打算完成此任务特征。


首先要了解的是 bash 环境(更准确地说 命令执行环境)与 POSIX 环境概念不同。 POSIX 环境 是非类型化 name= 的集合值对,并且可以以各种方式从进程传递给其子进程方式(实际上是IPC的有限形式)。

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

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


@lesmana 的答案中提到的限制是因为 POSIX 环境只是 name=value 对,没有额外的含义,因此没有一致的方法来编码或格式化类型变量,请参阅下面的一个有趣的 bash 怪癖函数 ,以及 bash-4.3 中即将发生的更改(提议的数组功能已放弃)。

有几种简单的方法可以使用 declare -p (内置)将一些 bash 环境输出为一组一个或多个 declare 语句,这些语句可用于重建“名称”的类型和值。这是基本的序列化,但是复杂性其他一些答案暗示。 declare -p 保留数组索引、稀疏数组和麻烦值的引用。对于数组的简单序列化,您可以逐行转储值,然后使用 read -a myarray 来恢复它(适用于连续的 0 索引数组,因为 read -a ) code> 自动分配索引)。

这些方法不需要对您将数组传递到的脚本进行任何修改。

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 在进入时不会遵循 BASH_ENV历史/POSIX 模式。

如果所有数组名称碰巧都有一个公共前缀,您可以使用 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 启用此行为,将导致 ( process) 环境,通过 printenv 可见:

myfoo=() { echo foo
}

变量为 functionname (或 functioname() 以实现向后兼容),其值为 ( ) { 函数体 }
当后续的 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 开始)对全局关联数组使用此方法 声明 -Ag 将按希望工作。)GNU parallel 文档对此方法有有用的变体,请参阅 --env 的讨论="http://www.gnu.org/software/parallel/man.html" rel="noreferrer">手册页


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

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

TL;DR: exportable arrays are not directly supported up to and including bash-5.1, but you can (effectively) export arrays in one of two ways:

  • a simple modification to the way the child scripts are invoked
  • use an exported function to store the array initialisation, with a simple modification to the child scripts

Or, you can wait until bash-4.3 is released (in development/RC state as of February 2014, see ARRAY_EXPORT in the Changelog). Update: This feature is not enabled in 4.3. If you define ARRAY_EXPORT when building, the build will fail. The author has stated it is not planned to complete this feature.


The first thing to understand is that the bash environment (more properly command execution environment) is different to the POSIX concept of an environment. The POSIX environment is a collection of un-typed name=value pairs, and can be passed from a process to its children in various ways (effectively a limited form of IPC).

The bash execution environment is effectively a superset of this, with typed variables, read-only and exportable flags, arrays, functions and more. This partly explains why the output of set (bash builtin) and env or printenv differ.

When you invoke another bash shell you're starting a new process, you loose some bash state. However, if you dot-source a script, the script is run in the same environment; or if you run a subshell via ( ) the environment is also preserved (because bash forks, preserving its complete state, rather than reinitialising using the process environment).


The limitation referenced in @lesmana's answer arises because the POSIX environment is simply name=value pairs with no extra meaning, so there's no agreed way to encode or format typed variables, see below for an interesting bash quirk regarding functions , and an upcoming change in bash-4.3(proposed array feature abandoned).

There are a couple of simple ways to do this using declare -p (built-in) to output some of the bash environment as a set of one or more declare statements which can be used reconstruct the type and value of a "name". This is basic serialisation, but with rather less of the complexity some of the other answers imply. declare -p preserves array indexes, sparse arrays and quoting of troublesome values. For simple serialisation of an array you could just dump the values line by line, and use read -a myarray to restore it (works with contiguous 0-indexed arrays, since read -a automatically assigns indexes).

These methods do not require any modification of the script(s) you are passing the arrays to.

declare -p array1 array2 > .bash_arrays       # serialise to an intermediate file
bash -c ". .bash_arrays; . otherscript.sh"    # source both in the same environment

Variations on the above bash -c "..." form are sometimes (mis-)used in crontabs to set variables.

Alternatives include:

declare -p array1 array2 > .bash_arrays       # serialise to an intermediate file
BASH_ENV=.bash_arrays otherscript.sh          # non-interactive startup script

Or, as a one-liner:

BASH_ENV=<(declare -p array1 array2) otherscript.sh

The last one uses process substitution to pass the output of the declare command as an rc script. (This method only works in bash-4.0 or later: earlier versions unconditionally fstat() rc files and use the size returned to read() the file in one go; a FIFO returns a size of 0, and so won't work as hoped.)

In a non-interactive shell (i.e. shell script) the file pointed to by the BASH_ENV variable is automatically sourced. You must make sure bash is correctly invoked, possibly using a shebang to invoke "bash" explicitly, and not #!/bin/sh as bash will not honour BASH_ENV when in historical/POSIX mode.

If all your array names happen to have a common prefix you can use declare -p ${!myprefix*} to expand a list of them, instead of enumerating them.

You probably should not attempt to export and re-import the entire bash environment using this method, some special bash variables and arrays are read-only, and there can be other side-effects when modifying special variables.

(You could also do something slightly disagreeable by serialising the array definition to an exportable variable, and using eval, but let's not encourage the use of 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")'

)


As referenced above, there's an interesting quirk: special support for exporting functions through the environment:

function myfoo() {
    echo foo
}

with export -f or set +a to enable this behaviour, will result in this in the (process) environment, visible with printenv:

myfoo=() { echo foo
}

The variable is functionname (or functioname() for backward compatibility) and its value is () { functionbody }.
When a subsequent bash process starts it will recreate a function from each such environment variable. If you peek into the bash-4.2 source file variables.c you'll see variables starting with () { are handled specially. (Though creating a function using this syntax with declare -f is forbidden.) Update: The "shellshock" security issue is related to this feature, contemporary systems may disable automatic function import from the environment as a mitigation.

If you keep reading though, you'll see an #if 0 (or #if ARRAY_EXPORT) guarding code that checks variables starting with ([ and ending with ), and a comment stating "Array variables may not yet be exported". The good news is that in the current development version bash-4.3rc2 the ability to export indexed arrays (not associative) is enabled. This feature is not likely to be enabled, as noted above.

We can use this to create a function which restores any array data required:

% function sharearray() {
    array1=(a b c d)
}

% export -f sharearray 

% bash -c 'sharearray; echo ${array1[*]}'

So, similar to the previous approach, invoke the child script with:

bash -c "sharearray; . otherscript.sh"

Or, you can conditionally invoke the sharearray function in the child script by adding at some appropriate point:

declare -F sharearray >/dev/null && sharearray

Note there is no declare -a in the sharearray function, if you do that the array is implicitly local to the function, not what is wanted. bash-4.2 supports declare -g that makes a variable declared in a function into a global, so declare -ga can then be used. (Since associative arrays require a declare -A you won't be able to use this method for global associative arrays prior to bash-4.2, from v4.2 declare -Ag will work as hoped.) The GNU parallel documentation has useful variation on this method, see the discussion of --env in the man page.


Your question as phrased also indicates you may be having problems with export itself. You can export a name after you've created or modified it. "exportable" is a flag or property of a variable, for convenience you can also set and export in a single statement. Up to bash-4.2 export expects only a name, either a simple (scalar) variable or function name are supported.

Even if you could (in future) export arrays, exporting selected indexes (a slice) may not be supported (though since arrays are sparse there's no reason it could not be allowed). Though bash also supports the syntax declare -a name[0], the subscript is ignored, and "name" is simply a normal indexed array.

挥剑断情 2024-11-06 20:30:25

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

在导出脚本中:(

myArray=( '  foo"bar  ' 

注意,双引号对于正确处理数组元素中的空格是必需的。)

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

'  foo"bar  ' 

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

eval "myArray=( ${myArray} )"

注意!如果您不能信任 myArrayeval代码>环境变量。这个技巧展示了“Little Bobby Tables”漏洞。想象一下,如果有人将 myArray 的值设置为 ) ; rm -rf / #

\n''\nbaz)' ) # an array with two nasty elements myArray="${myArray[@]@Q}" ./importing_script.sh

注意,双引号对于正确处理数组元素中的空格是必需的。)

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


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


注意!如果您不能信任 myArrayeval代码>环境变量。这个技巧展示了“Little Bobby Tables”漏洞。想象一下,如果有人将 myArray 的值设置为 ) ; rm -rf / #

\n\\nbaz)'

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


注意!如果您不能信任 myArrayeval代码>环境变量。这个技巧展示了“Little Bobby Tables”漏洞。想象一下,如果有人将 myArray 的值设置为 ) ; rm -rf / #

\n''\nbaz)' ) # an array with two nasty elements myArray="${myArray[@]@Q}" ./importing_script.sh

注意,双引号对于正确处理数组元素中的空格是必需的。)

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


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


注意!如果您不能信任 myArrayeval代码>环境变量。这个技巧展示了“Little Bobby Tables”漏洞。想象一下,如果有人将 myArray 的值设置为 ) ; rm -rf / #

Jeez. I don't know why the other answers made this so complicated. Bash has nearly built-in support for this.

In the exporting script:

myArray=( '  foo"bar  ' 

(Note, the double quotes are necessary for correct handling of whitespace within array elements.)

Upon entry to importing_script.sh, the value of the myArray environment variable comprises these exact 26 bytes:

'  foo"bar  ' 

Then the following will reconstitute the array:

eval "myArray=( ${myArray} )"

CAUTION! Do not eval like this if you cannot trust the source of the myArray environment variable. This trick exhibits the "Little Bobby Tables" vulnerability. Imagine if someone were to set the value of myArray to ) ; rm -rf / #.

\n''\nbaz)' ) # an array with two nasty elements myArray="${myArray[@]@Q}" ./importing_script.sh

(Note, the double quotes are necessary for correct handling of whitespace within array elements.)

Upon entry to importing_script.sh, the value of the myArray environment variable comprises these exact 26 bytes:


Then the following will reconstitute the array:


CAUTION! Do not eval like this if you cannot trust the source of the myArray environment variable. This trick exhibits the "Little Bobby Tables" vulnerability. Imagine if someone were to set the value of myArray to ) ; rm -rf / #.

\n\\nbaz)'

Then the following will reconstitute the array:


CAUTION! Do not eval like this if you cannot trust the source of the myArray environment variable. This trick exhibits the "Little Bobby Tables" vulnerability. Imagine if someone were to set the value of myArray to ) ; rm -rf / #.

\n''\nbaz)' ) # an array with two nasty elements myArray="${myArray[@]@Q}" ./importing_script.sh

(Note, the double quotes are necessary for correct handling of whitespace within array elements.)

Upon entry to importing_script.sh, the value of the myArray environment variable comprises these exact 26 bytes:


Then the following will reconstitute the array:


CAUTION! Do not eval like this if you cannot trust the source of the myArray environment variable. This trick exhibits the "Little Bobby Tables" vulnerability. Imagine if someone were to set the value of myArray to ) ; rm -rf / #.

幸福%小乖 2024-11-06 20:30:25

环境只是键值对的集合,它们都是字符串。适用于任何类型数组的正确解决方案可以将

  • 每个元素保存在不同的变量中(例如 MY_ARRAY_0=myArray[0])。由于动态变量名称而变得复杂。
  • 将数组保存在文件系统中(声明 -p myArray >file)。
  • 将所有数组元素序列化为单个字符串。

这些内容在其他帖子中都有介绍。如果您知道您的值从不包含特定字符(例如 |)并且您的键是连续整数,您可以简单地将数组保存为分隔列表:

export MY_ARRAY=$(IFS='|'; echo "${myArray[*]}")

并在子进程中恢复它:

IFS='|'; myArray=($MY_ARRAY); unset IFS

The environment is just a collection of key-value pairs, both of which are character strings. A proper solution that works for any kind of array could either

  • Save each element in a different variable (e.g. MY_ARRAY_0=myArray[0]). Gets complicated because of the dynamic variable names.
  • Save the array in the file system (declare -p myArray >file).
  • Serialize all array elements into a single string.

These are covered in the other posts. If you know that your values never contain a certain character (for example |) and your keys are consecutive integers, you can simply save the array as a delimited list:

export MY_ARRAY=$(IFS='|'; echo "${myArray[*]}")

And restore it in the child process:

IFS='|'; myArray=($MY_ARRAY); unset IFS
孤独陪着我 2024-11-06 20:30:25

基于 @mr.spuratic 对 BASH_ENV 的使用,这里我通过 script -f -c

script -c $@ 隧道命令> 可用于在另一个 pty(和进程组)内运行命令,但它不能将任何结构化参数传递给

相反, 是一个简单的字符串,作为system 库调用的参数。

我需要将外部 bash 的 $@ 隧道传输到脚本调用的 bash 的 $@ 中。

由于 declare -p 不能采用 @,这里我使用魔法 bash 变量 _ (带有一个虚拟的第一个数组值,因为它将被覆盖bash)。这使我不必践踏任何重要的变量:

概念证明:
BASH_ENV=<( 声明 -a _=("" "$@") && 声明 -p _ ) bash -c 'set -- "${_[@]:1}" & & echo "$@"'

“但是,”你说,“你正在向 bash 传递参数——事实上我也是如此,但这些是一个已知字符的简单字符串。这里是 script< 的使用/code>

SHELL=/bin/bash BASH_ENV=<( 声明 -a _=("" "$@") && 声明 -p _ && echo 'set -- "${ _[@]:1}"') script -f -c 'echo "$@"' /tmp/logfile

它给了我这个包装函数 in_pty:

in_pty( ){
SHELL=/bin/bash BASH_ENV=<( 声明 -a _=("" "$@") && 声明 -p _ && echo 'set -- "${_[@]:1 }"') script -f -c 'echo "$@"' /tmp/logfile
}

或将此无函数包装器作为 Makefile 的可组合字符串:

in_pty=bash -c 'SHELL=/bin/bash BASH_ENV=<(声明 -a _=("" "$$@ ") && 声明 -p _ && echo '"'"'set -- "$${_[@]:1}"'"'"') script -qfc '"'"'" $$@"'"'"' /tmp/logfile' --

...

$(in_pty) test --verbose $@ $^

Based on @mr.spuratic use of BASH_ENV, here I tunnel $@ through script -f -c

script -c <command> <logfile> can be used to run a command inside another pty (and process group) but it cannot pass any structured arguments to <command>.

Instead <command> is a simple string to be an argument to the system library call.

I need to tunnel $@ of the outer bash into $@ of the bash invoked by script.

As declare -p cannot take @, here I use the magic bash variable _ (with a dummy first array value as that will get overwritten by bash). This saves me trampling on any important variables:

Proof of concept:
BASH_ENV=<( declare -a _=("" "$@") && declare -p _ ) bash -c 'set -- "${_[@]:1}" && echo "$@"'

"But," you say, "you are passing arguments to bash -- and indeed I am, but these are a simple string of known character. Here is use by script

SHELL=/bin/bash BASH_ENV=<( declare -a _=("" "$@") && declare -p _ && echo 'set -- "${_[@]:1}"') script -f -c 'echo "$@"' /tmp/logfile

which gives me this wrapper function in_pty:

in_pty() {
SHELL=/bin/bash BASH_ENV=<( declare -a _=("" "$@") && declare -p _ && echo 'set -- "${_[@]:1}"') script -f -c 'echo "$@"' /tmp/logfile
}

or this function-less wrapper as a composable string for Makefiles:

in_pty=bash -c 'SHELL=/bin/bash BASH_ENV=<( declare -a _=("" "$$@") && declare -p _ && echo '"'"'set -- "$${_[@]:1}"'"'"') script -qfc '"'"'"$$@"'"'"' /tmp/logfile' --

...

$(in_pty) test --verbose $@ $^

再浓的妆也掩不了殇 2024-11-06 20:30:25

我正在编辑另一篇文章并犯了一个错误。啊啊。无论如何,也许这可能有帮助?
https://stackoverflow.com/a/11944320/1594168

请注意,因为 shell 的数组格式在 bash 或任何另一个壳的侧面,
以与平台无关的方式返回 shell 数组是非常困难的。
您必须检查版本,并制作一个简单的脚本来连接所有内容
shell 数组转换成其他进程可以解析的文件。

但是,如果您知道要带回家的数组的名称,那么还有一种方法,虽然有点脏。

可以说我有

MyAry[42]="whatever-stuff";
MyAry[55]="foo";
MyAry[99]="bar";

所以我想把它带回家

name_of_child=MyAry
take_me_home="`declare -p ${name_of_child}`";
export take_me_home="${take_me_home/#declare -a ${name_of_child}=/}"

我们可以通过从子进程检查

echo ""|awk '{print "from awk =["ENVIRON["take_me_home"]"]";  }'

结果来看到它正在导出:

from awk =['([42]="whatever-stuff" [55]="foo" [99]="bar")']

如果我们绝对必须,请使用环境变量来转储它。

env > some_tmp_file

然后

在运行另一个脚本之前,

# This is the magic that does it all
source some_tmp_file

I was editing a different post and made a mistake. Augh. Anyway, perhaps this might help?
https://stackoverflow.com/a/11944320/1594168

Note that because the shell's array format is undocumented on bash or any other shell's side,
it is very difficult to return a shell array in platform independent way.
You would have to check the version, and also craft a simple script that concatinates all
shell arrays into a file that other processes can resolve into.

However, if you know the name of the array you want to take back home then there is a way, while a bit dirty.

Lets say I have

MyAry[42]="whatever-stuff";
MyAry[55]="foo";
MyAry[99]="bar";

So I want to take it home

name_of_child=MyAry
take_me_home="`declare -p ${name_of_child}`";
export take_me_home="${take_me_home/#declare -a ${name_of_child}=/}"

We can see it being exported, by checking from a sub-process

echo ""|awk '{print "from awk =["ENVIRON["take_me_home"]"]";  }'

Result :

from awk =['([42]="whatever-stuff" [55]="foo" [99]="bar")']

If we absolutely must, use the env var to dump it.

env > some_tmp_file

Then

Before running the another script,

# This is the magic that does it all
source some_tmp_file
捂风挽笑 2024-11-06 20:30:25

正如 lesmana 所报告的,您无法导出数组。所以你必须在通过环境之前将它们序列化。这种序列化在其他只适合字符串的地方也很有用(su -c 'string'、ssh host 'string')。执行此操作的最短代码方法是滥用“getopt”,

# preserve_array(arguments). return in _RET a string that can be expanded
# later to recreate positional arguments. They can be restored with:
#   eval set -- "$_RET"
preserve_array() {
    _RET=$(getopt --shell sh --options "" -- -- "$@") && _RET=${_RET# --}
}

# restore_array(name, payload)
restore_array() {
   local name="$1" payload="$2"
   eval set -- "$payload"
   eval "unset $name && $name=("\$@")"
}

如下使用:

foo=("1: &&& - *" "2: two" "3: %# abc" )
preserve_array "${foo[@]}"
foo_stuffed=${_RET}
restore_array newfoo "$foo_stuffed"
for elem in "${newfoo[@]}"; do echo "$elem"; done

## output:
# 1: &&& - *
# 2: two
# 3: %# abc

这不会解决未设置/稀疏数组的问题。
您也许可以减少restore_array 中的2 次“eval”调用。

As lesmana reported, you cannot export arrays. So you have to serialize them before passing through the environment. This serialization useful other places too where only a string fits (su -c 'string', ssh host 'string'). The shortest code way to do this is to abuse 'getopt'

# preserve_array(arguments). return in _RET a string that can be expanded
# later to recreate positional arguments. They can be restored with:
#   eval set -- "$_RET"
preserve_array() {
    _RET=$(getopt --shell sh --options "" -- -- "$@") && _RET=${_RET# --}
}

# restore_array(name, payload)
restore_array() {
   local name="$1" payload="$2"
   eval set -- "$payload"
   eval "unset $name && $name=("\$@")"
}

Use it like this:

foo=("1: &&& - *" "2: two" "3: %# abc" )
preserve_array "${foo[@]}"
foo_stuffed=${_RET}
restore_array newfoo "$foo_stuffed"
for elem in "${newfoo[@]}"; do echo "$elem"; done

## output:
# 1: &&& - *
# 2: two
# 3: %# abc

This does not address unset/sparse arrays.
You might be able to reduce the 2 'eval' calls in restore_array.

南巷近海 2024-11-06 20:30:25

尽管这个问题/答案已经很老了,但在搜索“bash序列化数组”时,这篇文章似乎是最热门的

,而且,尽管原始问题与不太相关对于序列化/反序列化数组,答案似乎确实已经朝这个方向发展。

因此...我提供我的解决方案:

优点
  • 所有核心 Bash 概念
  • 无评估
  • 无子命令
缺点
  • 函数将变量名称作为参数(与实际值)
  • 序列化需要至少有一个字符出现在数组

serialize_array.bash

# shellcheck shell=bash

##
# serialize_array
# Serializes a bash array to a string, with a configurable seperator.
#
# $1 = source varname ( contains array to be serialized )
# $2 = target varname ( will contian the serialized string )
# $3 = seperator ( optional, defaults to 

中注意:这是作为要点托管在此处:

[编辑]

  • 通过 shellcheck + shfmt 运行后逻辑简化。
  • 添加了托管 GIST 的 URL
\x01' ) # # example: # # my_arry=( one "two three" four ) # serialize_array my_array my_string '|' # declare -p my_string # # result: # # declare -- my_string="one|two three|four" # function serialize_array() { declare -n _array="${1}" _str="${2}" # _array, _str => local reference vars local IFS="${3:-

中注意:这是作为要点托管在此处:

[编辑]

  • 通过 shellcheck + shfmt 运行后逻辑简化。
  • 添加了托管 GIST 的 URL
\x01'}" # shellcheck disable=SC2034 # Reference vars assumed used by caller _str="${_array[*]}" # * => join on IFS } ## # deserialize_array # Deserializes a string into a bash array, with a configurable seperator. # # $1 = source varname ( contains string to be deserialized ) # $2 = target varname ( will contain the deserialized array ) # $3 = seperator ( optional, defaults to

中注意:这是作为要点托管在此处:

[编辑]

  • 通过 shellcheck + shfmt 运行后逻辑简化。
  • 添加了托管 GIST 的 URL
\x01' ) # # example: # # my_string="one|two three|four" # deserialize_array my_string my_array '|' # declare -p my_array # # result: # # declare -a my_array=([0]="one" [1]="two three" [2]="four") # function deserialize_array() { IFS="${3:-

中注意:这是作为要点托管在此处:

[编辑]

  • 通过 shellcheck + shfmt 运行后逻辑简化。
  • 添加了托管 GIST 的 URL
\x01'}" read -r -a "${2}" <<<"${!1}" # -a => split on IFS }

中注意:这是作为要点托管在此处:

[编辑]

  • 通过 shellcheck + shfmt 运行后逻辑简化。
  • 添加了托管 GIST 的 URL

Although this question/answers are pretty old, this post seems to be the top hit when searching for "bash serialize array"

And, although the original question wasn't quite related to serializing/deserializing arrays, it does seem that the answers have devolved in that direction.

So with that ... I offer my solution:

Pros
  • All Core Bash Concepts
  • No Evals
  • No Sub-Commands
Cons
  • Functions take variable names as arguments (vs actual values)
  • Serializing requires having at least one character that is not present in the array

serialize_array.bash

# shellcheck shell=bash

##
# serialize_array
# Serializes a bash array to a string, with a configurable seperator.
#
# $1 = source varname ( contains array to be serialized )
# $2 = target varname ( will contian the serialized string )
# $3 = seperator ( optional, defaults to 

NOTE: This is hosted as a gist here:

[edits]

  • Logic simplified after running through shellcheck + shfmt.
  • Added URL for hosted GIST
\x01' ) # # example: # # my_arry=( one "two three" four ) # serialize_array my_array my_string '|' # declare -p my_string # # result: # # declare -- my_string="one|two three|four" # function serialize_array() { declare -n _array="${1}" _str="${2}" # _array, _str => local reference vars local IFS="${3:-

NOTE: This is hosted as a gist here:

[edits]

  • Logic simplified after running through shellcheck + shfmt.
  • Added URL for hosted GIST
\x01'}" # shellcheck disable=SC2034 # Reference vars assumed used by caller _str="${_array[*]}" # * => join on IFS } ## # deserialize_array # Deserializes a string into a bash array, with a configurable seperator. # # $1 = source varname ( contains string to be deserialized ) # $2 = target varname ( will contain the deserialized array ) # $3 = seperator ( optional, defaults to

NOTE: This is hosted as a gist here:

[edits]

  • Logic simplified after running through shellcheck + shfmt.
  • Added URL for hosted GIST
\x01' ) # # example: # # my_string="one|two three|four" # deserialize_array my_string my_array '|' # declare -p my_array # # result: # # declare -a my_array=([0]="one" [1]="two three" [2]="four") # function deserialize_array() { IFS="${3:-

NOTE: This is hosted as a gist here:

[edits]

  • Logic simplified after running through shellcheck + shfmt.
  • Added URL for hosted GIST
\x01'}" read -r -a "${2}" <<<"${!1}" # -a => split on IFS }

NOTE: This is hosted as a gist here:

[edits]

  • Logic simplified after running through shellcheck + shfmt.
  • Added URL for hosted GIST
嗳卜坏 2024-11-06 20:30:25

你(嗨!)可以使用这个,不需要编写文件,对于 ubuntu 12.04,bash 4.2.24

另外,你的多行数组可以导出。

cat >>exportArray.sh

function FUNCarrayRestore() {
    local l_arrayName=$1
    local l_exportedArrayName=${l_arrayName}_exportedArray

    # if set, recover its value to array
    if eval '[[ -n ${'$l_exportedArrayName'+dummy} ]]'; then
        eval $l_arrayName'='`eval 'echo 

在终端 bash 上测试此示例(适用于 bash 4.2.24):

source exportArray.sh
list=(a b c)
FUNCarrayFakeExport list
bash
echo ${list[@]} #empty :(
FUNCarrayRestore list
echo ${list[@]} #profit! :D

我可以改进它 此处

PS.:如果有人清除/改进/makeItRunFaster 我想知道/看到,谢谢! :D

$l_exportedArrayName` #do not put export here! fi } export -f FUNCarrayRestore function FUNCarrayFakeExport() { local l_arrayName=$1 local l_exportedArrayName=${l_arrayName}_exportedArray # prepare to be shown with export -p eval 'export '$l_arrayName # collect exportable array in string mode local l_export=`export -p \ |grep "^declare -ax $l_arrayName=" \ |sed 's"^declare -ax '$l_arrayName'"export '$l_exportedArrayName'"'` # creates exportable non array variable (at child shell) eval "$l_export" } export -f FUNCarrayFakeExport

在终端 bash 上测试此示例(适用于 bash 4.2.24):


我可以改进它 此处

PS.:如果有人清除/改进/makeItRunFaster 我想知道/看到,谢谢! :D

you (hi!) can use this, dont need writing a file, for ubuntu 12.04, bash 4.2.24

Also, your multiple lines array can be exported.

cat >>exportArray.sh

function FUNCarrayRestore() {
    local l_arrayName=$1
    local l_exportedArrayName=${l_arrayName}_exportedArray

    # if set, recover its value to array
    if eval '[[ -n ${'$l_exportedArrayName'+dummy} ]]'; then
        eval $l_arrayName'='`eval 'echo 

test this example on terminal bash (works with bash 4.2.24):

source exportArray.sh
list=(a b c)
FUNCarrayFakeExport list
bash
echo ${list[@]} #empty :(
FUNCarrayRestore list
echo ${list[@]} #profit! :D

I may improve it here

PS.: if someone clears/improve/makeItRunFaster I would like to know/see, thx! :D

$l_exportedArrayName` #do not put export here! fi } export -f FUNCarrayRestore function FUNCarrayFakeExport() { local l_arrayName=$1 local l_exportedArrayName=${l_arrayName}_exportedArray # prepare to be shown with export -p eval 'export '$l_arrayName # collect exportable array in string mode local l_export=`export -p \ |grep "^declare -ax $l_arrayName=" \ |sed 's"^declare -ax '$l_arrayName'"export '$l_exportedArrayName'"'` # creates exportable non array variable (at child shell) eval "$l_export" } export -f FUNCarrayFakeExport

test this example on terminal bash (works with bash 4.2.24):


I may improve it here

PS.: if someone clears/improve/makeItRunFaster I would like to know/see, thx! :D

沫雨熙 2024-11-06 20:30:25

对于值不带空格的数组,我一直在使用一组简单的函数来迭代每个数组元素并连接数组:

_arrayToStr(){
    array=($@)

    arrayString=""
    for (( i=0; i<${#array[@]}; i++ )); do
        if [[ $i == 0 ]]; then
            arrayString="\"${array[i]}\""
        else
            arrayString="${arrayString} \"${array[i]}\""
        fi
    done

    export arrayString="(${arrayString})"
}

_strToArray(){
    str=$1

    array=${str//\"/}
    array=(${array//[()]/""})

    export array=${array[@]}
}

第一个函数通过添加左括号和右括号并转义所有双引号。第二个函数将去掉引号和括号并将它们放入虚拟数组中。

为了导出数组,您需要传入原始数组的所有元素:

array=(foo bar)
_arrayToStr ${array[@]}

此时,数组已导出到值 $arrayString 中。要导入目标文件中的数组,请重命名该数组并执行相反的转换:

_strToArray "$arrayName"
newArray=(${array[@]})

For arrays with values without spaces, I've been using a simple set of functions to iterate through each array element and concatenate the array:

_arrayToStr(){
    array=($@)

    arrayString=""
    for (( i=0; i<${#array[@]}; i++ )); do
        if [[ $i == 0 ]]; then
            arrayString="\"${array[i]}\""
        else
            arrayString="${arrayString} \"${array[i]}\""
        fi
    done

    export arrayString="(${arrayString})"
}

_strToArray(){
    str=$1

    array=${str//\"/}
    array=(${array//[()]/""})

    export array=${array[@]}
}

The first function with turn the array into a string by adding the opening and closing parentheses and escaping all of the double quotation marks. The second function will strip the quotation marks and the parentheses and place them into a dummy array.

In order export the array, you would pass in all the elements of the original array:

array=(foo bar)
_arrayToStr ${array[@]}

At this point, the array has been exported into the value $arrayString. To import the array in the destination file, rename the array and do the opposite conversion:

_strToArray "$arrayName"
newArray=(${array[@]})
阿楠 2024-11-06 20:30:25

非常感谢@stéphane-chazelas,他指出了我之前尝试中的所有问题,现在这似乎可以将数组序列化到标准输出或变量中。

此技术不会对输入进行 shell 解析(与 declare -a/declare -p 不同),因此可以安全地防止在序列化文本中恶意插入元字符。

注意:换行符不会被转义,因为 read 会删除 \ 字符对,因此必须传递 -d ...读取,然后保留未转义的换行符。

所有这些都在unserialise 函数中进行管理。

使用两个魔术字符,字段分隔符和记录分隔符(以便多个数组可以序列化到同一个流)。

这些字符可以定义为 FSRS,但都不能定义为 newline 字符,因为转义的换行符会被 read< 删除/代码>。

转义字符必须是 \ 反斜杠,因为 read 使用它来避免字符被识别为 IFS 字符。

serialise 将把 "$@" 序列化到 stdout,serialise_to 将序列化为 $1 中命名的变量

serialise() {
  set -- "${@//\\/\\\\}" # \
  set -- "${@//${FS:-;}/\\${FS:-;}}" # ; - our field separator
  set -- "${@//${RS:-:}/\\${RS:-:}}" # ; - our record separator
  local IFS="${FS:-;}"
  printf ${SERIALIZE_TARGET:+-v"$SERIALIZE_TARGET"} "%s" "$*${RS:-:}"
}
serialise_to() {
  SERIALIZE_TARGET="$1" serialise "${@:2}"
}
unserialise() {
  local IFS="${FS:-;}"
  if test -n "$2"
  then read -d "${RS:-:}" -a "$1" <<<"${*:2}"
  else read -d "${RS:-:}" -a "$1"
  fi
}

,并使用反序列化:

unserialise data # read from stdin

unserialise data "$serialised_data" # from args

例如

$ serialise "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" 

(没有尾随换行符)

读回它:

$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" 

或者

unserialise array # read from stdin

Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:

serialise_array "${my_array[@]}"

你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:

while unserialise array
do ...
done
Party\tParty\tParty' Now is the time;For all good men;To drink $drink;At the `party`;Party Party Party:

(没有尾随换行符)

读回它:


或者


Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:


你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:


Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/

或者


Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:


你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:


Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party   Party   Party:

(没有尾随换行符)

读回它:


或者


Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:


你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:


\n'}"

Now is the time 
For all good men 
To drink $drink 
At the `party` 
Party   Party   Party

或者


Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:


你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:


Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party   Party   Party:

(没有尾随换行符)

读回它:


或者


Bash 的 read 尊重转义字符 \ (除非您传递 -r 标志)以删除字符的特殊含义,例如 for输入字段分隔或行定界。

如果你想序列化一个数组而不是单纯的参数列表,那么只需将你的数组作为参数列表传递:


你可以在循环中使用unserialise,就像read一样,因为它只是一个包装的读取 - 但请记住该流不是换行符分隔的:


Much thanks to @stéphane-chazelas who pointed out all the problems with my previous attempts, this now seems to work to serialise an array to stdout or into a variable.

This technique does not shell-parse the input (unlike declare -a/declare -p) and so is safe against malicious insertion of metacharacters in the serialised text.

Note: newlines are not escaped, because read deletes the \<newlines> character pair, so -d ... must instead be passed to read, and then unescaped newlines are preserved.

All this is managed in the unserialise function.

Two magic characters are used, the field separator and the record separator (so that multiple arrays can be serialized to the same stream).

These characters can be defined as FS and RS but neither can be defined as newline character because an escaped newline is deleted by read.

The escape character must be \ the backslash, as that is what is used by read to avoid the character being recognized as an IFS character.

serialise will serialise "$@" to stdout, serialise_to will serialise to the varable named in $1

serialise() {
  set -- "${@//\\/\\\\}" # \
  set -- "${@//${FS:-;}/\\${FS:-;}}" # ; - our field separator
  set -- "${@//${RS:-:}/\\${RS:-:}}" # ; - our record separator
  local IFS="${FS:-;}"
  printf ${SERIALIZE_TARGET:+-v"$SERIALIZE_TARGET"} "%s" "$*${RS:-:}"
}
serialise_to() {
  SERIALIZE_TARGET="$1" serialise "${@:2}"
}
unserialise() {
  local IFS="${FS:-;}"
  if test -n "$2"
  then read -d "${RS:-:}" -a "$1" <<<"${*:2}"
  else read -d "${RS:-:}" -a "$1"
  fi
}

and unserialise with:

unserialise data # read from stdin

or

unserialise data "$serialised_data" # from args

e.g.

$ serialise "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" 

(without a trailing newline)

read it back:

$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" 

or

unserialise array # read from stdin

Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:

serialise_array "${my_array[@]}"

You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:

while unserialise array
do ...
done
Party\tParty\tParty' Now is the time;For all good men;To drink $drink;At the `party`;Party Party Party:

(without a trailing newline)

read it back:


or


Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:


You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:


Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/

or


Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:


You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:


Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party   Party   Party:

(without a trailing newline)

read it back:


or


Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:


You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:


\n'}"

Now is the time 
For all good men 
To drink $drink 
At the `party` 
Party   Party   Party

or


Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:


You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:


Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party   Party   Party:

(without a trailing newline)

read it back:


or


Bash's read respects the escape character \ (unless you pass the -r flag) to remove special meaning of characters such as for input field separation or line delimiting.

If you want to serialise an array instead of a mere argument list then just pass your array as the argument list:


You can use unserialise in a loop like you would read because it is just a wrapped read - but remember that the stream is not newline separated:



    
时光沙漏 2024-11-06 20:30:25

我为此编写了自己的函数,并使用 IFS 改进了该方法:

功能

  • 不调用 $(...) 因此不会产生另一个 bash shell 进程
  • ?| 字符序列化为 ?00?01序列和返回,因此可以在带有这些字符的数组上使用
  • 处理序列化/反序列化之间的行返回字符作为其他字符
  • cygwin bash 3.2.48Linux bash 4.3.48中测试代码>
function tkl_declare_global()
{
  eval "$1=\"\$2\"" # right argument does NOT evaluate
}

function tkl_declare_global_array()
{
  local IFS=

示例:

a=(
 \t\r\n' # just in case, workaround for the bug in the "[@]:i" expression under the bash version lower than 4.1
  eval "$1=(\"\${@:2}\")"
}

function tkl_serialize_array()
{
  local __array_var="$1"
  local __out_var="$2"

  [[ -z "$__array_var" ]] && return 1
  [[ -z "$__out_var" ]] && return 2

  local __array_var_size
  eval declare "__array_var_size=\${#$__array_var[@]}"

  (( ! __array_var_size )) && { tkl_declare_global $__out_var ''; return 0; }

  local __escaped_array_str=''

  local __index
  local __value
  for (( __index=0; __index < __array_var_size; __index++ )); do
    eval declare "__value=\"\${$__array_var[__index]}\""
    __value="${__value//\?/?00}"
    __value="${__value//|/?01}"
    __escaped_array_str="$__escaped_array_str${__escaped_array_str:+|}$__value"
  done

  tkl_declare_global $__out_var "$__escaped_array_str"

  return 0
}

function tkl_deserialize_array()
{
  local __serialized_array="$1"
  local __out_var="$2"

  [[ -z "$__out_var" ]] && return 1
  (( ! ${#__serialized_array} )) && { tkl_declare_global $__out_var ''; return 0; }

  local IFS='|'
  local __deserialized_array=($__serialized_array)

  tkl_declare_global_array $__out_var

  local __index=0
  local __value
  for __value in "${__deserialized_array[@]}"; do
    __value="${__value//\?01/|}"
    __value="${__value//\?00/?}"
    tkl_declare_global $__out_var[__index] "$__value"
    (( __index++ ))
  done

  return 0
}

示例:


1 \n 2' "3\"4'" 5 '|' '?')
tkl_serialize_array a b
tkl_deserialize_array "$b" c
\t\r\n' # just in case, workaround for the bug in the "[@]:i" expression under the bash version lower than 4.1 eval "$1=(\"\${@:2}\")" } function tkl_serialize_array() { local __array_var="$1" local __out_var="$2" [[ -z "$__array_var" ]] && return 1 [[ -z "$__out_var" ]] && return 2 local __array_var_size eval declare "__array_var_size=\${#$__array_var[@]}" (( ! __array_var_size )) && { tkl_declare_global $__out_var ''; return 0; } local __escaped_array_str='' local __index local __value for (( __index=0; __index < __array_var_size; __index++ )); do eval declare "__value=\"\${$__array_var[__index]}\"" __value="${__value//\?/?00}" __value="${__value//|/?01}" __escaped_array_str="$__escaped_array_str${__escaped_array_str:+|}$__value" done tkl_declare_global $__out_var "$__escaped_array_str" return 0 } function tkl_deserialize_array() { local __serialized_array="$1" local __out_var="$2" [[ -z "$__out_var" ]] && return 1 (( ! ${#__serialized_array} )) && { tkl_declare_global $__out_var ''; return 0; } local IFS='|' local __deserialized_array=($__serialized_array) tkl_declare_global_array $__out_var local __index=0 local __value for __value in "${__deserialized_array[@]}"; do __value="${__value//\?01/|}" __value="${__value//\?00/?}" tkl_declare_global $__out_var[__index] "$__value" (( __index++ )) done return 0 }

示例:


I've wrote my own functions for this and improved the method with the IFS:

Features:

  • Doesn't call to $(...) and so doesn't spawn another bash shell process
  • Serializes ? and | characters into ?00 and ?01 sequences and back, so can be used over array with these characters
  • Handles the line return characters between serialization/deserialization as other characters
  • Tested in cygwin bash 3.2.48 and Linux bash 4.3.48
function tkl_declare_global()
{
  eval "$1=\"\$2\"" # right argument does NOT evaluate
}

function tkl_declare_global_array()
{
  local IFS=

Example:

a=(
 \t\r\n' # just in case, workaround for the bug in the "[@]:i" expression under the bash version lower than 4.1
  eval "$1=(\"\${@:2}\")"
}

function tkl_serialize_array()
{
  local __array_var="$1"
  local __out_var="$2"

  [[ -z "$__array_var" ]] && return 1
  [[ -z "$__out_var" ]] && return 2

  local __array_var_size
  eval declare "__array_var_size=\${#$__array_var[@]}"

  (( ! __array_var_size )) && { tkl_declare_global $__out_var ''; return 0; }

  local __escaped_array_str=''

  local __index
  local __value
  for (( __index=0; __index < __array_var_size; __index++ )); do
    eval declare "__value=\"\${$__array_var[__index]}\""
    __value="${__value//\?/?00}"
    __value="${__value//|/?01}"
    __escaped_array_str="$__escaped_array_str${__escaped_array_str:+|}$__value"
  done

  tkl_declare_global $__out_var "$__escaped_array_str"

  return 0
}

function tkl_deserialize_array()
{
  local __serialized_array="$1"
  local __out_var="$2"

  [[ -z "$__out_var" ]] && return 1
  (( ! ${#__serialized_array} )) && { tkl_declare_global $__out_var ''; return 0; }

  local IFS='|'
  local __deserialized_array=($__serialized_array)

  tkl_declare_global_array $__out_var

  local __index=0
  local __value
  for __value in "${__deserialized_array[@]}"; do
    __value="${__value//\?01/|}"
    __value="${__value//\?00/?}"
    tkl_declare_global $__out_var[__index] "$__value"
    (( __index++ ))
  done

  return 0
}

Example:


1 \n 2' "3\"4'" 5 '|' '?')
tkl_serialize_array a b
tkl_deserialize_array "$b" c
\t\r\n' # just in case, workaround for the bug in the "[@]:i" expression under the bash version lower than 4.1 eval "$1=(\"\${@:2}\")" } function tkl_serialize_array() { local __array_var="$1" local __out_var="$2" [[ -z "$__array_var" ]] && return 1 [[ -z "$__out_var" ]] && return 2 local __array_var_size eval declare "__array_var_size=\${#$__array_var[@]}" (( ! __array_var_size )) && { tkl_declare_global $__out_var ''; return 0; } local __escaped_array_str='' local __index local __value for (( __index=0; __index < __array_var_size; __index++ )); do eval declare "__value=\"\${$__array_var[__index]}\"" __value="${__value//\?/?00}" __value="${__value//|/?01}" __escaped_array_str="$__escaped_array_str${__escaped_array_str:+|}$__value" done tkl_declare_global $__out_var "$__escaped_array_str" return 0 } function tkl_deserialize_array() { local __serialized_array="$1" local __out_var="$2" [[ -z "$__out_var" ]] && return 1 (( ! ${#__serialized_array} )) && { tkl_declare_global $__out_var ''; return 0; } local IFS='|' local __deserialized_array=($__serialized_array) tkl_declare_global_array $__out_var local __index=0 local __value for __value in "${__deserialized_array[@]}"; do __value="${__value//\?01/|}" __value="${__value//\?00/?}" tkl_declare_global $__out_var[__index] "$__value" (( __index++ )) done return 0 }

Example:



    
欢你一世 2024-11-06 20:30:25

我认为你可以尝试这种方式(通过在导出后采购你的脚本):

export myArray=(Hello World)

。你的脚本.sh

I think you can try it this way (by sourcing your script after export):

export myArray=(Hello World)

. yourScript.sh

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文