附加到其名称作为bash函数参数传递的数组

时间:2017-01-28 21:13:10

标签: bash

我正在编写一个函数,它在参数:

中传递的数组末尾添加一个元素
#@function add_elem_to_array: add an element to an array 
#in:
#1 name of the array
#2 element to add

add_elem_to_array()
{
   elem=$1  
   array=$2        
   index=${#array[@]} #get the index where to insert 

   eval "$array[$index]=$elem" #!!!! The problem is here
}

你能帮我解决一下这个问题吗?

4 个答案:

答案 0 :(得分:3)

假设bash 4.3或更新,因此拥有namevars(declare -n / local -n):

add_elem_to_array() {
  local elem=$1 array_name=$2
  local -n array=$array_name
  array+=( "$elem" )
}

支持bash 3.x(特别包括3.2,在撰写本文时广泛使用的最旧版本):

add_elem_to_array() {
  local elem=$1 array_name=$2
  local cmd
  printf -v cmd '%q+=( %q )' "$array_name" "$elem"
  eval "$cmd"
}

那就是说 - 给定array+=( "$value" )作为可用的语法,为此目的几乎不需要函数,是吗?

答案 1 :(得分:3)

我不会为此使用函数:

array+=("$elem")

附加一个元素。

如果您真的想使用某个功能并且Bash 4.3或更新版本,则可以使用nameref:

add_elem_to_array () {
    local elem=$1
    local -n arr=$2
    arr+=("$elem")
}

答案 2 :(得分:2)

Charles Duffy's answer适用于Bash 4.3+,但如果您使用旧版本的Bash,则没有简单的解决方案(除非您因为某些可怕的原因而希望使用eval。)但是,确实可以做到!

这是我掀起的:

## Arguments:
## $1 - the element to append
## $2 - the name of the array
append_to_array () {
    local -ia 'keys=(-1 "${!'$2'[@]}")';
    local IFS='';
    read -r -d '' -n ${#1} "$2"[${keys[${#keys[@]}-1]}+1] <<< "$1";
}

说明:

间接可能很棘手并且让我永远学习,但它强大而有趣,所以我想我会解释一切如何融合在一起。

让我们使用arr作为数组的名称 将元素追加到具有arr+=(1)arr+=("first element appended" "second element appended")之类的数组时,数组中元素的索引(键)对每个元素只增加1。例如:

$ declare -a arr=(A)
$ arr+=(B)
$ arr+=(C D)
$ declare -p arr
declare -a arr='([0]="A" [1]="B" [2]="C" [3]="D")'
$ echo ${#arr[@]}
4

您可以看到数组的大小等于数组的下一个可用索引,但情况并非总是如此。继续:

$ arr[7648]="E"
$ arr+=(F)
$ echo ${#arr[@]}
6
$ declare -p arr
declare -a arr='([0]="A" [1]="B" [2]="C" [3]="D" [7648]="E" [7649]="F")'

第1行:
这就是为什么在我的函数的第一行,我从keys的索引创建一个整数数组a${!arr[@]}扩展到arr的索引。 keys中的最后一个元素应该比我们想要放置新元素的索引少一个。但是,如果arr未设置或为空,${!arr[@]}将扩展为空,所以我放{在-1的前面{1}}来处理此问题。

第2行:
接下来,我们清除keys(使用IFS以避免在函数外部更改它)以确保保留附加元素中的任何尾随或前导空格字符。在不清除local的情况下,IFS和此处字符串运算符read将从<<<中删除前导和尾随空格字符,这是不可取的。

第3行:
在第三行中,我们使用"$1"read中的值复制到"$1"引用的数组中。 $2阻止-r处理/解释read中的特殊字符,"$1"选项将分隔符设置为空字符,以允许我们的元素包含换行符(我会来回到-d ''选项。)。
-n ${#1}计算${#keys[@]}-1中最后一个元素的索引,因此keys获取键的最后一个元素并向其中添加一个元素,形成我们所需的索引以放置${keys[${#keys[@]}-1]}+1

"$1"命令可用于写入数组中的元素,例如read可以替换为arr[2]="hi",但read arr[2] <<< "hi"也适用于对数组的间接引用,因此我们也可以执行readnam=arr; read ${nam}[2] <<< "hi"并生成相同的结果。这就是i=2; nam=arr; read ${nam}[$i] <<< "hi"能够将read -r -d '' -n ${#1} ${2}[${keys[${#keys[@]}-1]}+1] <<< "$1"附加到"$1"引用的数组的原因。

最后,由于我不知道的原因,$2是必需的。当我第一次编写脚本时,每个附加元素都附加了换行符。我不知道为什么会这样,所以希望其他人可以分享一些见解。所以我只是通过将读取的字符数限制为-n ${#1}中的字符数来解决这个问题。

可以附加任意数量的元素和健全性检查参数的改进版本:

"$1"

答案 3 :(得分:0)

您的问题实际上在您确定索引的行中。

   eval index=\${#${array}[@]} #get the index where to insert

您需要使用eval展开包含数组名称的变量,然后展开表达式以获取其长度。转义第一个美元符号会抑制数组长度请求的扩展,直到eval之后。

脚本的其余部分似乎按预期工作。这是我使用的调试版本,以便显示正在发生的事情:

add_elem_to_array()
{
   elem=$1
   array=$2
   eval index=\${#${array}[@]} #get the index where to insert

   echo "elem ='$elem'"
   echo "array='$array'"
   echo "index='$index'"

   eval "$array[$index]=$elem" #!!!! The problem is here
}

arr=(This is a test)
echo "arr = '${arr[@]}'"
add_elem_to_array "one" arr
echo "arr = '${arr[@]}'"
add_elem_to_array "two" arr
echo "arr = '${arr[@]}'"