如何使用间接引用迭代数组?

Neu*_*ino 20 bash scripting indirection

如何使此代码有效?

#!/bin/bash
ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )
for FRUIT in ${!ARRAYNAME[@]}
do
    echo ${FRUIT}
done
Run Code Online (Sandbox Code Playgroud)

这段代码:

echo ${!ARRAYNAME[0]}
Run Code Online (Sandbox Code Playgroud)

打印APPLE.我尝试做类似的事情,但用"[@]"迭代数组.

提前致谢,

Tim*_*ote 28

${!ARRAYNAME[@]}指"指数ARRAYNAME".正如bash手册页中所述,因为ARRAYNAME设置,但作为字符串,而不是数组,它返回0.

这是一个使用的解决方案eval.

#!/usr/bin/env bash

ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )

eval array=\( \${${ARRAYNAME}[@]} \)

for fruit in "${array[@]}"; do
  echo ${fruit}
done
Run Code Online (Sandbox Code Playgroud)

您最初尝试做的是创建间接参考.这些是在bash版本2中引入的,并且在很大程度上取代了eval在shell中尝试实现类似反射的行为时的需要.

在对数组使用间接引用时,您需要做的是包含[@]对变量名称的猜测:

#!/usr/bin/env bash

ARRAYNAME='FRUITS'
FRUITS=( APPLE BANANA ORANGE )

array="${ARRAYNAME}[@]"
for fruit in "${!array}"; do
  echo $fruit
done
Run Code Online (Sandbox Code Playgroud)

总而言之,在这个简单的例子中使用间接引用是一回事,但是,正如Dennis Williamson提供的链接所示,你应该对在真实世界的脚本中使用它们犹豫不决.它们几乎可以保证使您的代码比必要的更令人困惑.通常,您可以使用关联数组获得所需的功能.

  • @Neuquino如果ARRAYNAME是您脚本的输入参数,那么您正在做一些非常错误的事情.将用户输入与变量名混合是没有理由的.在这种情况下(结合间接扩展),它允许任意代码注入.这些技术的唯一正当理由是在函数中使用,从不在全局范围内,并且永远不会与用户输入结合使用. (2认同)

Rob*_*ade 13

这是一种没有eval的方法.

请参阅此处描述的Bash技巧#2:http: //mywiki.wooledge.org/BashFAQ/006

似乎在bash 3及以上版本中工作.

#!/bin/bash

ARRAYNAME='FRUITS'
tmp=$ARRAYNAME[@]
FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" )
for FRUIT in "${!tmp}"
do
    echo "${FRUIT}"
done
Run Code Online (Sandbox Code Playgroud)

这是一个更现实的例子,展示了如何通过引用函数传递数组:

pretty_print_array () {
  local arrayname=$1
  local tmp=$arrayname[@]
  local array=( "${!tmp}" )
  local FS=', ' # Field seperator
  local var
  # Print each element enclosed in quotes and separated by $FS
  printf -v var "\"%s\"$FS" "${array[@]}"
  # Chop trailing $FS
  var=${var%$FS}
  echo "$arrayname=($var)"
}
FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" )
pretty_print_array FRUITS
# prints FRUITS=("APPLE", "BANANA", "ORANGE", "STAR FRUIT")
Run Code Online (Sandbox Code Playgroud)


Han*_*der 7

eval执行包含数组元素的代码,即使它们包含,例如,命令替换。它还通过解释其中的 bash 元字符来更改数组元素。

避免这些问题的工具是declare reference,请参阅man bash下面的声明:

-n 为每个名称赋予 nameref 属性,使其成为对另一个变量的名称引用。另一个变量由 name 的值定义。除了使用或更改 -n 属性本身的那些引用、赋值和属性修改之外,所有对 name 的引用、赋值和属性修改都是在 name 值引用的变量上执行的。nameref 属性不能应用于数组变量。

#!/bin/bash
declare -n ARRAYNAME='FRUITS'
FRUITS=(APPLE BANANA ORANGE "BITTER LEMON")
for FRUIT in "${ARRAYNAME[@]}"
do
    echo "${FRUIT}"
done
Run Code Online (Sandbox Code Playgroud)


Cro*_*max 7

这个答案来得很晚,但我想有比目前提出的方法更干净的方法(尊重他们的作者)。

这是关于使用/ bash 内置-n选项的。(有关更多信息,请在 bash 中输入)。declarelocalhelp declare

那么我们开始吧:

ARRAYNAME='FRUITS';
FRUITS=(APPLE BANANA ORANGE);

# This is the critical addition. With help of option `-n` we declare
# variable `fruits` as indirect reference to another variable. Anytime
# we refer to ${fruits} we would actually refer to a variable whose
# name is stored in `fruits` variable:
declare -n fruits="${ARRAYNAME}";

# Here we use ${fruits} as ordinary variable, but in reality it refers
# to `FRUITS` variable:
for FRUIT in ${fruits[@]}; do
    echo "${FRUIT}";
done;
Run Code Online (Sandbox Code Playgroud)

结果是:

APPLE
BANANA
ORANGE
Run Code Online (Sandbox Code Playgroud)