“后端口”;声明-n“;从bash4.4到4.2

“后端口”;声明-n“;从bash4.4到4.2,bash,Bash,在Bash4.4中,我使用“declare-n”编写脚本,但今天我了解到,当我将这些脚本提供给Redhat7用户时,脚本会失败,因为他们的Bash是4.2 下面是一个小的问题示例,我想知道您是否可以给我提供一个好的方法,以便将此备份到BASH 4.2: #!/bin/bash pwd=`pwd` declare -A parms parms[engine]=\"Sweave\" parms[verbose]=FALSE parms[tangle]=TRUE ## builds $parmst

在Bash4.4中,我使用“declare-n”编写脚本,但今天我了解到,当我将这些脚本提供给Redhat7用户时,脚本会失败,因为他们的Bash是4.2

下面是一个小的问题示例,我想知道您是否可以给我提供一个好的方法,以便将此备份到BASH 4.2:

#!/bin/bash

pwd=`pwd`
declare -A parms
parms[engine]=\"Sweave\"
parms[verbose]=FALSE
parms[tangle]=TRUE

## builds $parmstring by concatenating key=value pairs
catarr() {
        declare -n __p="$1"
        for k in "${!__p[@]}"
        do parmstring+=", $k=${__p[$k]}"
        done
}
parmstring=""
catarr parms

echo ${parmstring[*]}
输出应该是这样的:

$ bash bashmre.sh
, engine="Sweave", verbose=FALSE, tangle=TRUE

但在旧的BASH上,RedHat说declare不允许“-n”。

它一点也不干净,但您可以生成并执行代码。为确保生成安全,请使用
printf%q
替换值

catarr() {
  local eval_str

  printf -v eval_str '
    for k in "${!%q[@]}"; do
      parmstring+=", $k=${%q[$k]}"
    done
  ' "$1" "$1"
  eval "$eval_str"
}

我认为您所寻找的可以通过
eval
实现:

#!/bin/bash

pwd=`pwd`
declare -A parms
parms[engine]=\"Sweave\"
parms[verbose]=FALSE
parms[tangle]=TRUE

## builds $parmstring by concatenating key=value pairs
catarr() {
    eval keys=(\"\${!$1[@]}\")
    for k in "${keys[@]}"
    do
        eval val=\${$1[$k]}
        parmstring+=", $k=$val"
    done
}
parmstring=""
catarr parms

echo ${parmstring[*]}

我在Bash4.2.37上测试了这个问题,得到了所需的输出

我给@Charles Duffy和@shay的答案加了+1,但最后我没有使用这两个答案

我采取了查尔斯在我的原始帖子评论中建议的方法。我重新编写了这个东西,因此需要连接的每个数组都有一个单独的函数。在实际的应用程序中,我们正在使用它,有两个数组,“parms”和“myopts”,因此我最终得到了名为catparms和catmyopts的函数

catparms(){
    for k in "${!parms[@]}"
    do parmstring+=", $k=${parms[$k]}"
    done
}

catmyopts(){
    for k in "${!myopts[@]}"
    do optstring+=", $k=${myopts[$k]}"
    done
}
如果我有很多数组在周围浮动,那么这个方法就会变得单调乏味


如果发生这种情况,我很确定我会回到依赖bash4.4的方法,告诉那些使用旧Bash的人,他们必须升级,否则就忘了它。我需要学习如何在脚本中添加代码,以检测当前的Bash版本,并在它不是4.3或更高版本时终止。我没有做过,但是可以。

如果你没有内置支持就可以获得该功能的效果,那么添加内置支持又有什么意义呢?顺便说一句,
paramstring+=“foo”
只是在
paramstring[0]
后面追加,所以
[*]
没有意义,您是否曾经将
catar
用于除
parms
以外的目标?如果没有,请硬编码数组名称,就完成了。谢谢。我不明白,但它是有效的,所以对我来说是+1。我希望在座的其他人投票,帮助选择正确的答案。你能看看@shay的建议吗?我觉得还可以。你有什么意见吗?我在一篇评论中注意到shay的答案中有一个错误,但这很容易修复(通过添加额外的引号)。这种方法的更大问题(在不转义的情况下将
$1
替换为
eval
的字符串)是通过恶意变量名进行代码注入——如果有人运行
catar'}foo[@]});运行任意命令;#',此版本只会抛出一个错误,并且该版本将运行所选的任意命令。如果攻击者无论如何都可以运行任意命令,那么这不是一个风险,但是如果您正在从正在读取的数据(文件名等)生成变量名,那么这就是一个风险。请参阅针对其他代码的成功代码注入攻击的示例,即使使用额外的引号进行编辑;与之相比,对该答案尝试相同的攻击,该答案会发出语法错误,但实际上不会运行有效负载。需要在
eval
'd字符串中加引号——现在您正在运行
keys=(${!parms[@]})
,这是不正确的;它需要是
keys=(“${!parms[@]}”)
才能正确处理所有可能的键。使用
parms[“扩展键”]=“两个单词”
进行测试更好。我建议让它只
echo“$parmstring”
——因为字符串只是一个字符串而不是一个数组,所以没有必要将其视为后者——但这只是一种诡辩。我同意。我只是想尽可能少地修改示例脚本