如何在不展平数组的情况下从bash数组中删除元素

Wil*_*ett 0 bash

我想创建一个像这样的bash数组的函数:

a=("element zero" "element one" "element two")
Run Code Online (Sandbox Code Playgroud)

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

a=("element zero" "element two")
Run Code Online (Sandbox Code Playgroud)

这样echo $a[1]会打印出来element two而不打印出来zero.

我已经看过几次尝试,但是没有找到一个干净利落地完成它或者没有破坏多个元素空间的元素.或者只是将元素设置为空白(即不移动后续数组元素的索引).

Cha*_*ffy 5

# 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
Run Code Online (Sandbox Code Playgroud)

...现在,对于一个函数,如果你有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
}
Run Code Online (Sandbox Code Playgroud)

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

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

的使用printf%q格式字符串是有点偏执狂的-它使得更难恶意选择的值(在这种情况下,变量名)来执行他们的选择动作,而不是简单地用无影响失败.


所有这一切 - 如果你重新编号阵列,那就更好了.如果你不进行重新编号步骤,那么在删除条目之后a[1]你只需要一个稀疏数组,该索引没有内容(这与该索引处的空字符串不同 - bash"数组"实际上存储为链表或哈希表[在关联的情况下],根本不是数组,所以稀疏数组是内存有效的,删除操作要快得多.

如果您检索向数组请求其键而不是从外部提供它们,这不会破坏您迭代数组的能力,如:

for key in "${!a[@]}"; do
  value="${a[$key]}"
  echo "Entry $key has value $value"
done
Run Code Online (Sandbox Code Playgroud)