我四处寻找“我可以使用数组作为环境变量,然后从 Bash shell 脚本调用它”这个问题的答案,并得出结论,它没有得到“官方”支持(概述)这里, 和这里)。
然而,我确实需要这样做,并想出了一个“解决方法”,并希望得到您的意见。
设置:我在文件中创建了一个变量.profile
,如下所示:
HOST_NAMES='server1 server2 server3'
然后在 shell 脚本的开头我写道:
SRVR_ARRAY=($(echo $HOST_NAMES | awk '{for (i = 1; i <=NF; i++) print $i " "}'))
后来在脚本中,当需要做一些工作时,我调用了:
for h in "${SRVR_ARRAY[@]}"; do
ping $h
done
它成功了!现在我确信这是 Bash shell 脚本 jerry-riggin 最好的版本,但想看看是否有人能看到这种用法的任何风险?
答案1
您可能面临的问题是常见的:分裂和通配符。
字符串中不能包含空格,因为它们将被拆分为单独的数组项,并且任何类似于全局字符 ( *?[]
) 的内容都将扩展为匹配的文件名。虽然主机名可能不是问题,但总的来说,确实如此。
这是一个常见问题,反复讨论,参见例如 shell 变量的扩展以及 glob 和 split 对它的影响 和 忘记在 bash/POSIX shell 中引用变量的安全隐患
要解决这些问题,您需要 a) 设置IFS
为空格以外的其他内容,并用它分隔字符串,b) 禁用set -f
.
这将用作|
分隔符并分割字符串。您可以使用值中不需要的任何字符,甚至可能是一些控制字符,例如\001
($'\001'
在 Bash 中使用来创建它)。
#!/bin/bash
string='foo|bar'
IFS='|'; set -f
array=($string) # split it
IFS='|'
string="${array[*]}" # merge it back together
另外,就像他们在评论中所说的那样,如果您的变量只有主机名并且您可以在空格上拆分,则不需要 awk 来拆分它。 shell 将在分配给数组或启动循环时执行此操作:
SRVR_ARRAY=( $HOST_NAMES )
for x in $HOST_NAMES ; do ...
HOST_NAMES
(同样,如果包含全局字符,您会遇到麻烦。)
实际上,即使在您的示例中也会发生这种情况:awk
打印一些内容,shell 捕获它,将其拆分为单词,因为$()
不在双引号内,然后将单词单独保存在数组中。
答案2
您可以将输出存储declare -p
在环境变量中:
array=(foo 'bar baz'); array[12]=sparse
export ARRAY_definition="$(declare -p array)"
然后,在执行的bash
脚本中:
eval "$ARRAY_definition"
请注意,重要的是要eval
在与 相同的语言环境中执行declare
(最好是相同的版本bash
)
如果eval
不在全局范围内运行,则将声明该数组当地的。
使用zsh
,您可以使用引用来避免使用危险的eval
:
export ARRAY_definition=${(j: :)${(q)array}}
导入:
array=(${(Q)${(z)ARRAY_definition}})
rc
或者,您可以使用类似,es
或fish
支持本机导出数组(使用它们自己的编码)的shell 。