间接返回数组中的所有元素


10

Bash手册页描述了使用${!a}来返回变量名称的内容,该变量的名称是内容的名称a(间接级别)。

我想知道如何使用此方法返回数组中的所有元素,即

a=(one two three)
echo ${a[*]}

退货

one two three

我想要:

b=a
echo ${!b[*]}

返回相同的。不幸的是,它不是,而是返回0

更新资料

有了这些答复,我现在意识到我的示例太简单了,因为类似这样:

b=("${a[@]}")

完全可以实现我所说的需求。

所以,这就是我想要做的:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

当然,仔细阅读Bash手册页会发现这并不符合预期,因为最后一行只是返回“数组”的索引$_LIST(根本不是数组)。

在任何情况下,都应执行以下操作(如前所述):

LIST=($(eval echo \${$_LIST[*]}))

或...(最终我走的路线):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

当然,假设元素不包含空格。


添加[@]到指针_LIST="LIST_${whichone}[@]",然后LIST=("${!_LIST}")用于复制数组。最好使用小写的变量名,以避免与环境变量(全大写)冲突。
艾萨克(Isaac)

Answers:


7

我认为应从字面上对待使用bash变量的间接引用。

例如。对于原始示例:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

对于最后一个真实场景,我相信下面的代码可以完成工作。

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

最好使用"${var[@]}"避免与$IFSand参数扩展混淆的符号。这是最终代码。

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted

8

您需要显式复制元素。对于索引数组:

b=("${a[@]}")

对于关联数组(请注意,这a是数组变量的名称,而不是值是数组变量名称的变量):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

如果数组中有变量名,则可以使用逐元素方法以及额外的步骤来检索键。

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(警告,本文中的代码是直接在浏览器中键入的,未经测试。)


您说的很对,但是很遗憾,这不能解决我的问题(我的示例过于简单)。我已经提供了更新,以使我的意图更加清楚。
埃里克·史密斯

@Eric我认为在ksh / bash中,您需要在那个阶段进行评估。看到我的编辑。
吉尔(Gilles)“所以,别再邪恶了”,

+1,但是根据您的免责声明,这实际上并不太有效(代码的最后一部分),但只需要进行少量调整即可。具体来说,它应该\${!$name[@]}在第一行上,以便第一个扩展名仅是'$ name',并且${!a[@]}将e保存为eval,并将相同的内容保存在for循环中\${$name}。那里也不一定非要在“ $ k”前加上其他两个反斜杠。
krb686

2

${!b[*]}扩展为array中使用的索引b

您需要分两步完成操作,因此eval会有所帮助:eval echo \${$b[*]}。(请注意,\这可确保第一步$将通过第一步,即变量扩展,并且仅在第二步中通过进行扩展eval。)

根据参数扩展!,两者都用于间接扩展({!a}),与前缀匹配的名称(${!a*})和数组键列表(${!a[*]})。由于数组键列表的语法与预期的间接扩展+数组元素扩展的语法相同,因此不支持后者。


2
${!a}确实扩展为名称为的变量的值$a。手册中的段落以“如果参数的第一个字符是感叹号(!),则引入了变量间接访问级别”开头的段落中对此进行了简洁的描述。
吉尔(Gilles)“所以,别再邪恶了”,

是的-@Gilles是正确的,但是@manatwork在二读时,我注意到这${!有点含糊,因为如果您要处理的是数组,则行为会有所不同。
埃里克·史密斯

@Gilles,您在这句话上是正确的,但遗憾的是,它不适用于“这是$$ !! prefix *}和$ {!name [@]}的扩展例外,如下所述。” 但是我的回答肯定是模棱两可,所以我将对其进行编辑。
manatwork 2011年

0

要间接访问数组,只需添加[@]到indirect变量b=a[@]

如果设置了此变量:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

然后,这将起作用:

b="a[@]"
printf '<%s> ' "${!b}"

或者简单地:

echo "${!b}"

这样的数组可以这样复制:

newArr=( "${!b}" )

然后打印:

declare -p newArr

在一个脚本中:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

当然,以上所有内容都会复制一个非稀疏数组。其中所有索引都具有值的一种。

稀疏数组

稀疏数组是可能具有未定义元素的数组。
例如,a[8]=1234定义一个元素,并且在bash中,0到7不存在。

要复制这种稀疏数组,请使用此方法

  1. 打印旧数组:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. 替换数组的名称并捕获字符串:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. 评估这样创建的字符串,就定义了新数组:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.