【问题标题】:How to remove an element from a bash array without flattening the array如何在不展平数组的情况下从 bash 数组中删除元素
【发布时间】:2014-08-23 01:08:25
【问题描述】:

我想做一个函数,它接受一个像这样的 bash 数组:

a=("element zero" "element one" "element two")

并删除一个元素,如“元素一”,并留下这样的数组:

a=("element zero" "element two")

这样echo $a[1] 将打印出element two 而不是zero

我已经看到了几次尝试,但还没有找到一个干净利落的或没有将具有空格的元素分解为多个元素的尝试。或者只是将元素设置为空白(即不移动后续数组元素的索引)。

【问题讨论】:

    标签: bash


    【解决方案1】:
    # initial state
    a=( "first element" "second element" "third element" )
    
    # to remove
    unset a[0]
    
    # to reindex, such that a[0] is the old a[1], rather than having the array
    # start at a[1] with no a[0] entry at all
    a=( "${a[@]}" )
    
    # to print the array with its indexes, to check its state at any stage
    declare -p a
    

    ...现在,对于一个函数,如果您有 bash 4.3,则可以使用 namevars 来执行此操作,而无需任何 eval

    remove() {
      local -n _arr=$1      # underscore-prefixed name to reduce collision likelihood
      local idx=$2
      unset _arr[$idx]      # remove the undesired item
      _arr=( "${_arr[@]}" ) # renumber the indexes
    }
    

    对于旧版本的 bash,它有点粘:

    remove() {
      local cmd
      unset "$1[$2]"
      printf -v cmd '%q=( "${%q[@]}" )' "$1" "$1" && eval "$cmd"
    }
    

    printf%q 格式字符串一起使用有点偏执——它使恶意选择的值(在本例中为变量名)更难执行他们选择的操作,而不是简单地失败没有效果。


    说了这么多——最好不要重新编号数组。如果您放弃重新编号步骤,这样在删除条目 a[1] 后,您只需在该索引 处拥有一个没有内容的稀疏数组(这与该索引处的空字符串不同——bash“数组”实际上是存储为链表或哈希表[在关联情况下],根本不是数组,因此稀疏数组是内存效率的),删除操作要快得多。

    如果您检索时向数组询问其键而不是在外部提供它们,这不会破坏您遍历数组的能力,如下所示:

    for key in "${!a[@]}"; do
      value="${a[$key]}"
      echo "Entry $key has value $value"
    done
    

    【讨论】:

    • “${!a[@]}”中的键的顺序是否保证在取消设置元素后仍会在数值上增加? (我的意思是,就bash 提供保证而言:))
    • 对于非关联数组,是的。
    • Charles,对于你的非函数示例,在 unset a[0] 和 reindex 之后我得到 declare -a a='([0]="second" [1]="element" [2]="third" [3]="element")' 是否需要在 reindex 上防止分词?那是declare -p a的输出
    • @CharlesDuffy 测试,我发现你需要a=( "${a[@]}" ) 来防止分词。
    • @DavidC.Rankin,确实有一个。我在哪里说、暗示或展示相反的东西?
    【解决方案2】:
    remove() { 
        eval "$1=( \"\${$1[@]:0:$2}\" \"\${$1[@]:$(($2+1))}\" )" 
    }
    

    可以这样调用:

    a=("element zero" "element one" "element two")
    remove a 1
    echo ${a[@]}  #element zero element two
    echo ${a[1]}  #element two
    

    这将留下空白数组元素。

    a=("element zero" "element one" "" "element three")
    remove a 1
    echo ${a[@]}  #element zero element two
    echo ${a[1]}  #
    echo ${a[2]}  #element three
    

    这将展平稀疏数组中未设置的元素。

    【讨论】:

      猜你喜欢
      • 2014-11-10
      • 2019-04-04
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      相关资源
      最近更新 更多