35

我怎样才能使这段代码工作?

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

这段代码:

echo ${!ARRAYNAME[0]}

打印苹果。我正在尝试做类似的事情,但使用“[@]”来迭代数组。

提前致谢,

4

7 回答 7

41

${!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

您最初尝试做的是创建一个Indirect Reference。这些是在 bash 版本 2 中引入的,旨在在很大程度上取代eval尝试在 shell 中实现类似反射的行为时的需要。

在对数组使用间接引用时,您必须做的是[@]在您对变量名的猜测中包含以下内容:

#!/usr/bin/env bash

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

array="${ARRAYNAME}[@]"
for fruit in "${!array}"; do
  echo $fruit
done

综上所述,在这个简单的示例中使用间接引用是一回事,但是,正如 Dennis Williamson 提供的链接中所指出的,您应该对在实际脚本中使用它们犹豫不决。它们几乎可以保证使您的代码比必要的更加混乱。通常,您可以使用关联数组获得所需的功能。

于 2012-06-24T20:07:16.443 回答
20

这是一种无需评估的方法。

请参阅此处描述的 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

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

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")
于 2014-09-17T00:56:13.190 回答
7

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

避免这些问题的工具是declare 参考,参见man bash声明:

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

#!/bin/bash
declare -n ARRAYNAME='FRUITS'
FRUITS=(APPLE BANANA ORANGE "BITTER LEMON")
for FRUIT in "${ARRAYNAME[@]}"
do
    echo "${FRUIT}"
done
于 2017-11-05T16:14:11.563 回答
3

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

它是关于使用/ 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;

结果是:

APPLE
BANANA
ORANGE
于 2020-02-23T23:59:58.287 回答
0

尽管有一个简单的 OP 问题,但这些答案不适用于最常见的实际用例,即包含不应扩展为文件名的空格或通配符的数组元素。

FRUITS=( APPLE BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY=\(\${$ARRAYNAME[@]}\)

$ echo "${ARRAY[4]}"
broken
$ echo "${ARRAY[5]}"
config.h
$

这有效:

FRUITS=( APPLE BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY="(\"\${$ARRAYNAME[@]}\")"

$ echo "${ARRAY[3]}"
not broken
$ echo "${ARRAY[4]}"
*.h
$

正如您应该养成使用"$@"not的习惯一样$@,始终在内部引用( )以进行数组扩展,除非您想要文件名扩展或知道数组元素不可能包含空格。

做这个:X=("${Y[@]}")

不是这个:X=(${Y[@]})

于 2014-04-30T01:59:29.163 回答
0

我认为对他的问题的正确方法和最佳答案与实际的间接引用有关,对提问者的原始代码进行最少的修改,您甚至可以使用关联数组来做到这一点。

OP 的最少修改代码

declare -n ARRAYNAME='FRUITS'
declare -a FRUITS=( APPLE BANANA ORANGE )
for FRUIT in ${!ARRAYNAME[@]}
do
    echo "${ARRAYNAME[${FRUIT}]}"
done

输出

APPLE
BANANA
ORANGE

在关联数组中的用法

declare -A associative_array
declare -n array_name=associative_array
associative_array[kittens]='cat'
associative_array[puppies]='dog'
associative_array[kitties]='cat'
associative_array[doggies]='dog'
for name in ${!array_name[@]} ; do 
echo $name has the value of "${associative_array[$name]}"
done

输出:

puppies has the value of dog
kittens has the value of cat
kitties has the value of cat
doggies has the value of dog

无需阅读整个 bash 手册页,只需使用内置help(

$ help help
help: help [-dms] [pattern ...]
    Display information about builtin commands.
    
    Displays brief summaries of builtin commands.  If PATTERN is
    specified, gives detailed help on all commands matching PATTERN,
    otherwise the list of help topics is printed.
    
    Options:
      -d    output short description for each topic
      -m    display usage in pseudo-manpage format
      -s    output only a short usage synopsis for each topic matching
            PATTERN
    
    Arguments:
      PATTERN   Pattern specifying a help topic
    
    Exit Status:
    Returns success unless PATTERN is not found or an invalid option is given.

)

声明的用法:

declare: declare [-aAfFgilnrtux] [-p] [name[=value] ...]
    Set variable values and attributes.
    
    Declare variables and give them attributes.  If no NAMEs are given,
    display the attributes and values of all variables.
    
    Options:
      -f    restrict action or display to function names and definitions
      -F    restrict display to function names only (plus line number and
            source file when debugging)
      -g    create global variables when used in a shell function; otherwise
            ignored
      -p    display the attributes and value of each NAME
    
    Options which set attributes:
      -a    to make NAMEs indexed arrays (if supported)
      -A    to make NAMEs associative arrays (if supported)
      -i    to make NAMEs have the `integer' attribute
      -l    to convert the value of each NAME to lower case on assignment
      -n    make NAME a reference to the variable named by its value
      -r    to make NAMEs readonly
      -t    to make NAMEs have the `trace' attribute
      -u    to convert the value of each NAME to upper case on assignment
      -x    to make NAMEs export
    
    Using `+' instead of `-' turns off the given attribute.
    
    Variables with the integer attribute have arithmetic evaluation (see
    the `let' command) performed when the variable is assigned a value.
    
    When used in a function, `declare' makes NAMEs local, as with the `local'
    command.  The `-g' option suppresses this behavior.
    
    Exit Status:
    Returns success unless an invalid option is supplied or a variable
    assignment error occurs.
于 2021-12-14T19:10:54.490 回答
-1

我只是想添加另一个有用的用例。我在网上搜索一个不同但相关的问题的解决方案

ARRAYNAME=( FRUITS VEG )
FRUITS=( APPLE BANANA ORANGE )
VEG=( CARROT CELERY CUCUMBER )
for I in "${ARRAYNAME[@]}"
do
    array="${I}[@]"
    for fruit in "${!array}"; do
        echo $fruit
    done
done
于 2013-11-08T05:12:48.007 回答