Arrays 在bash脚本中导出数组
我无法将数组从一个bash脚本导出到另一个bash脚本,如下所示:Arrays 在bash脚本中导出数组,arrays,bash,export,Arrays,Bash,Export,我无法将数组从一个bash脚本导出到另一个bash脚本,如下所示: export myArray[0]="Hello" export myArray[1]="World" foo=("1: &&& - *" "2: two" "3: %# abc" ) preserve_array "${foo[@]}" foo_stuffed=${_RET} restore_array newfoo "$foo_stuffed" for elem in "${newfoo[@]}";
export myArray[0]="Hello"
export myArray[1]="World"
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
当我这样写的时候,没有问题:
export myArray=("Hello" "World")
出于几个原因,我需要将数组初始化为多行。你有什么解决办法吗
数组变量可能尚未导出
来自ubuntu 10.04下bash 4.1.5版的主页
Chet Ramey(截至2011年的bash维护人员)的以下声明可能是关于这个“bug”的最正式的文档:
将数组变量编码到环境中并不是一个好方法
我在编辑另一篇文章时出错了。啊。不管怎样,也许这会有帮助? 注意,由于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 var来转储它
env > some_tmp_file
然后
在运行另一个脚本之前
# This is the magic that does it all
source some_tmp_file
你(嗨!)可以使用这个,不需要写文件,对于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 $'$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一起使用):
我可以改进它
注:如果有人清除/改进/使之成为我想知道/看到的,谢谢!:D正如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=("\$@")"
}
像这样使用它:
export myArray[0]="Hello"
export myArray[1]="World"
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_数组中的2个“eval”调用 TL;DR:在bash-4.3之前(包括bash-4.3)都不直接支持可导出阵列,但您可以(有效地)通过以下两种方式之一导出阵列:
- 对子脚本调用方式的简单修改
- 使用导出的函数存储数组初始化,并对子脚本进行简单修改
ARRAY\u EXPORT
,则生成将失败。作者不打算完成此功能
首先要了解的是bash环境(更恰当地说)不同于POSIX环境概念。是未键入的
name=value
对的集合,可以从进程传递给其子进程(实际上是一种有限的形式)
bash执行环境实际上是其中的一个超集,具有类型化变量、只读和可导出标志、数组、函数等。这部分解释了为什么set
(bash内置)和env
或printenv
的输出不同
当您调用另一个bashshell时,您正在启动一个新进程,您失去了一些bash状态。但是,如果您点源一个脚本,该脚本将在相同的环境中运行;或者,如果通过()
运行子shell,环境也会被保留(因为bash分叉,保留其完整状态,而不是使用流程环境重新初始化)
@lesmana的答案中引用的限制是因为POSIX环境是简单的
name=value
对,没有额外的意义,因此没有商定的编码或格式化类型化变量的方法,请参阅下面关于函数的有趣bash怪癖,以及bash-4.3中即将进行的更改(建议放弃的数组特性)
有两种简单的方法可以使用declare-p
(内置)将一些bash环境输出为一组或多个declare
语句,这些语句可以用来重构“name”的类型和值。这是最基本的,但其他一些答案所暗示的内容要少一些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
)
如上所述,有一个有趣的怪癖:通过环境导出函数的特殊支持:
function myfoo() {
echo foo
}
使用export-f
或set+a
启用此行为,将导致在(流程)环境中出现此情况,使用printenv
可见:
myfoo=() { echo foo
}
变量为functionname
(或functionme()
,用于向后兼容),其值为({functionbody}
)。
当后续bash进程启动时,它将从每个这样的环境变量重新创建一个函数。如果您查看bash-4.2源文件variables.c
,您将看到以()开头的变量{
是经过特殊处理的。(尽管禁止使用declare-f
的此语法创建函数。)更新:“安全问题与此功能相关,同时。”
% function sharearray() {
array1=(a b c d)
}
% export -f sharearray
% bash -c 'sharearray; echo ${array1[*]}'
bash -c "sharearray; . otherscript.sh"
[ "`type -t sharearray`" = "function" ] && sharearray
_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[@]}
_strToArray "$arrayName"
newArray=(${array[@]})
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\`" $'Party\tParty\tParty'
Now is the time;For all good men;To drink $drink;At the `party`;Party Party Party:
$ serialise_to s "Now is the time" "For all good men" "To drink \$drink" "At the \`party\`" $'Party\tParty\tParty'
$ unserialise array "$s"
$ echo "${array[@]/#/$'\n'}"
Now is the time
For all good men
To drink $drink
At the `party`
Party Party Party
unserialise array # read from stdin
serialise_array "${my_array[@]}"
while unserialise array
do ...
done
export MY_ARRAY=$(IFS='|'; echo "${myArray[*]}")
IFS='|'; myArray=($MY_ARRAY); unset IFS
function tkl_declare_global()
{
eval "$1=\"\$2\"" # right argument does NOT evaluate
}
function tkl_declare_global_array()
{
local IFS=$' \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
}
a=($'1 \n 2' "3\"4'" 5 '|' '?')
tkl_serialize_array a b
tkl_deserialize_array "$b" c