在Bash中假设以下内容:
declare -A ar='([one]="1" [two]="2" )'
declare -a ari='([0]="one" [1]="two")'
for i in ${!ari[@]}; do
echo $i ${ari[i]} ${ar[${ari[i]}]}
done
0 one 1
1 two 2
使用GNU Parallel可以做同样的事情,确保使用关联数组的索引,而不是序列吗?数组无法导出的事实是否会使这很困难,如果不是不可能的话?
答案 0 :(得分:3)
是的,它变得更加棘手。但并非不可能。
您无法直接导出数组 。但是,您可以使用declare -p
将数组转换为同一数组的描述,并且可以将该描述存储在可导出变量中。实际上,您可以将该描述存储在函数中并导出该函数,尽管它有点像黑客,并且您必须处理在函数内执行declare
命令使得声明的事实变量本地,所以你需要
在生成的-g
函数中引入declare
标记。
更新:自shellshock以来,上述黑客攻击无效。主题的一个小变化确实有效。因此,如果您的bash已更新,请跳至副标题" ShellShock版本"。
所以,这是生成可导出函数的一种可能方法:
make_importer () {
local func=$1; shift;
export $func='() {
'"$(for arr in $@; do
declare -p $arr|sed '1s/declare -./&g/'
done)"'
}'
}
现在我们可以创建我们的数组并为它们构建一个导出的导入器:
$ declare -A ar='([one]="1" [two]="2" )'
$ declare -a ari='([0]="one" [1]="two")'
$ make_importer ar_importer ar ari
看看我们建立了什么
$ echo "$ar_importer"
() {
declare -Ag ar='([one]="1" [two]="2" )'
declare -ag ari='([0]="one" [1]="two")'
}
好的,格式化有点难看,但这不是关于空格的。不过,这就是黑客攻击。我们所有人都有一个普通的(尽管是导出的)变量,但是当它被导入子shell时,会发生一些魔法[注1]:
$ bash -c 'echo "$ar_importer"'
$ bash -c 'type ar_importer'
ar_importer is a function
ar_importer ()
{
declare -Ag ar='([one]="1" [two]="2" )';
declare -ag ari='([0]="one" [1]="two")'
}
它看起来也更漂亮。
现在我们可以在我们给parallel
的命令中运行它:
$ printf %s\\n ${!ari[@]} |
parallel \
'ar_importer; echo "{}" "${ari[{}]}" "${ar[${ari[{}]}]}"'
0 one 1
1 two 2
或者,要在远程计算机上执行:
$ printf %s\\n ${!ari[@]} |
parallel -S localhost --env ar_importer \
'ar_importer; echo "{}" "${ari[{}]}" "${ar[${ari[{}]}]}"'
0 one 1
1 two 2
不幸的是,对shellshock的一系列修复使得完成同样的任务变得有点困难。特别是,现在需要将名为foo
的函数导出为名为BASH_FUNC_foo%%
的环境变量,这是一个无效的名称(因为百分号)。但是我们仍然可以定义函数(使用eval
)并将其导出,如下所示:
make_importer () {
local func=$1; shift;
# An alternative to eval is:
# . /dev/stdin <<< ...
# but that is neither less nor more dangerous than eval.
eval "$func"'() {
'"$(for arr in $@; do
declare -p $arr|sed '1s/declare -./&g/'
done)"'
}'
export -f "$func"
}
如上所述,我们可以构建数组并创建导出器:
$ declare -A ar='([one]="1" [two]="2" )'
$ declare -a ari='([0]="one" [1]="two")'
$ make_importer ar_importer ar ari
但是现在,该函数实际上作为函数存在于我们的环境中:
$ type ar_importer
ar_importer is a function
ar_importer ()
{
declare -Ag ar='([one]="1" [two]="2" )';
declare -ag ari='([0]="one" [1]="two")'
}
由于它已导出,我们可以在我们提供给parallel
的命令中运行它:
$ printf %s\\n ${!ari[@]} |
parallel \
'ar_importer; echo "{}" "${ari[{}]}" "${ar[${ari[{}]}]}"'
0 one 1
1 two 2
不幸的是,它不再适用于远程计算机(至少我使用的parallel
版本),因为parallel
不知道如何导出函数。如果这个问题得到解决,则以下内容应该有效:
$ printf %s\\n ${!ari[@]} |
parallel -S localhost --env ar_importer \
'ar_importer; echo "{}" "${ari[{}]}" "${ar[${ari[{}]}]}"'
但是,有一个重要的警告:你不能将带有shellshock补丁的bash中的函数导出到没有补丁的bash,反之亦然。因此,即使parallel
得到修复,远程计算机也必须运行与本地计算机相同的bash版本。 (或者至少,两者都必须或两者都没有shellshock补丁。)
注1 :神奇的是,bash
将导出变量标记为函数的方式是导出值与() {
完全一致。因此,如果您导出一个以这些字符开头并且语法正确的函数的变量,那么bash
个子shell将把它视为一个函数。 (不要期望非bash
个子壳能够理解。)
答案 1 :(得分:1)
GNU Parallel是一个perl程序。如果perl程序无法访问变量,那么我没有看到perl程序可以传递变量的方法。
因此,如果您想并行化循环,我会看到两个选项:
declare -A ar='([one]="1" [two]="2" )'
declare -a ari='([0]="one" [1]="two")'
for i in ${!ari[@]}; do
sem -j+0 echo $i ${ari[i]} ${ar[${ari[i]}]}
done
sem
解决方案无法防范混合输出。
declare -A ar='([one]="1" [two]="2" )'
declare -a ari='([0]="one" [1]="two")'
for i in ${!ari[@]}; do
echo echo $i ${ari[i]} ${ar[${ari[i]}]}
done | parallel
答案 2 :(得分:1)
四年来发生了很多事情。 GNU Parallel 20190222带有env_parallel
。这是一个Shell函数,可以将大部分环境导出到GNU Parallel运行的命令。
ash
,bash
,csh
,dash
,fish
,ksh
,mksh
,{ {1}},pdksh
,sh
和tcsh
。外壳对支持的支持各不相同(请参见https://www.gnu.org/software/parallel/env_parallel.html的详细信息)。对于zsh
,您可以这样做:
bash
因此您的情况如下:
# Load the env_parallel function
. `which env_parallel.bash`
# Ignore variables currently defined
env_parallel --session
[... define your arrays, functions, aliases, and variables here ...]
env_parallel my_command ::: values
# The environment is also exported to remote systems if they use the same shell
(echo value1; echo value2) | env_parallel -Sserver1,server2 my_command
# Optional cleanup
env_parallel --end-session
如您所料,env_parallel --session
declare -A ar='([one]="1" [two]="2" )'
declare -a ari='([0]="one" [1]="two")'
foo() {
for i in ${!ari[@]}; do
echo $i ${ari[i]} ${ar[${ari[i]}]}
done;
}
env_parallel foo ::: dummy
env_parallel --end-session
比纯env_parallel
慢一点。