首页 > 解决方案 > Inconsistencies when removing elements from a Bash array

问题描述

When using bash arrays, you can remove an element in the following manner:

unset array[i]   # where i is the array index

The problem with this is after the unset ${array[@]] is not truly valid. Yes it does give you the number of active array elements, but not the actual array depth. The index of the removed index in the array still exists. It simply has been set inactive/null. For example:

declare -a array=( a b c d e )
unset array[2]

for ((i=0; i < ${#array[@]}; i++)) ; do
    echo "$i: ${array[$i]}"
done

outputs the following:

0: a
1: b
2:
3: d

array[2] is still there but set to null or inactive

array[4] (e) does not show because ${#array[@]} is the number of active array elements and not the true array element depth. This gets very messy very quickly each time an element is unset

As an example consider the following code:

# array contains 0 or more strings # remove looks for and removes a given string

remove () {

    local str=$1

    for (( i = 0 ; i < ${#array[@]}; i++ )) ; do
        if [[ "${array[$i]}" == "$str" ]] ; then
            unset array[$i]
            return 0
        fi
    done

    echo "$str: not registered"

    return 0
}

This is only valid the first time remove is called. After that, valid elements may be missed.

One fix for this is to added the following line after the unset:

     unset array[$i]
  +  array=( "${array[@]}" )

This re-initializes array with the element completely removed.

The problem, this feels kludgy.

So my questions are this:

1) is there a way of getting the true array element depth?

2) is there a way of detecting the end of an array when iterating through it?

3) is there another cleaner solution?

标签: arraysbash

解决方案


了解为什么 for ((i=0; i<${#array[@]}; i++))会损坏

从您在这里要求的意义上说,没有“真正的数组元素深度”之类的东西。Bash 数组并不是真正的数组——它们是哈希映射(常规数组的数字索引,bash 4.0 的新“关联数组”的字符串索引)。因此,键绝对没有理由从 0 开始——你可以有一个像下面这样的数组:declare -a array=( [1000]="one thousand" [2000]="two thousand" [3000]="three thousand" ),它的长度正好是 3;这些项目之间没有一堆 NUL/空元素(分别用 keys和1000查找)。20003000


安全地从稀疏数组中删除元素

如果要按索引删除,请遍历索引。虽然"${array[@]}"迭代数组中的项目,但"${!array[@]}"(注意!)通过可以查找这些项目的索引进行迭代。

正如您所观察到的,假设索引范围从 0 到数组中的项目总数是不安全的,因为 bash 数组可以是稀疏的——但是没有理由编写代码来在第一名。

for array_idx in "${!array[@]}"; do
  unset "array[$array_idx]"
done

推荐阅读