从Bash数组中删除元素

Ale*_*lex 91 arrays variables bash

我需要从bash shell中的数组中删除一个元素.一般来说,我只是这样做:

array=("${(@)array:#<element to remove>}")
Run Code Online (Sandbox Code Playgroud)

不幸的是,我想删除的元素是一个变量,所以我不能使用上一个命令.在这里举个例子:

array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Run Code Online (Sandbox Code Playgroud)

任何的想法?

che*_*ner 137

下面的作品,你会喜欢bashzsh:

$ array=(pluto pippo)
$ delete=(pluto)
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
Run Code Online (Sandbox Code Playgroud)

如果需要删除多个元素:

...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
   array=("${array[@]/$del}") #Quotes when working with strings
done
Run Code Online (Sandbox Code Playgroud)

警告

这种技术实际上删除$delete了与元素匹配的前缀,而不一定是整个元素.

更新

要真正删除确切的项目,您需要遍历数组,将目标与每个元素进行比较,并使用unset删除完全匹配.

array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
  for i in "${!array[@]}"; do
    if [[ ${array[i]} = $target ]]; then
      unset 'array[i]'
    fi
  done
done
Run Code Online (Sandbox Code Playgroud)

请注意,如果执行此操作,并且删除了一个或多个元素,则索引将不再是连续的整数序列.

$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
Run Code Online (Sandbox Code Playgroud)

简单的事实是,数组并非设计用作可变数据结构.它们主要用于在单个变量中存储项目列表,而不需要将字符作为分隔符(例如,存储可包含空格的字符串列表).

如果间隙是个问题,那么您需要重建阵列以填补空白:

for i in "${!array[@]}"; do
    new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
Run Code Online (Sandbox Code Playgroud)

  • 只知道:`$ array =(sun sunflower)``$ delete =(sun)``$ echo $ {array [@]/$ delete}`导致`flower` (32认同)
  • 请注意,这实际上是在替换,所以如果数组类似于`(pluto1 pluto2 pippo)`那么你最终会得到`(1 2 pippo)`. (9认同)
  • 注意:这可能会将相应的值设置为空,但元素仍将在数组中. (4认同)
  • 为了重新创建数组,因为间隙必须消失,所以以下内容就足够了: `arr=("${arr[@]}")` (4认同)
  • 请小心在for循环中使用它,因为你最终会得到一个空元素,其中删除的元素是.为了理智,你可以做一些像'for {$ [array [@]}中的元素那样做[if [[$ element]]; 然后回显$ {element} fi done` (3认同)
  • 那么如何只删除匹配的元素呢? (2认同)

Ste*_*let 26

您可以构建一个没有不需要的元素的新数组,然后将其分配回旧数组.这适用于bash:

array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
    [[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
Run Code Online (Sandbox Code Playgroud)

这会产生:

echo "${array[@]}"
pippo
Run Code Online (Sandbox Code Playgroud)


小智 10

如果知道值的位置,这是取消设置值的最直接方法。

$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
Run Code Online (Sandbox Code Playgroud)

  • 尝试`echo $ {array [1]}`,您将获得空字符串。要获得“三个”,您需要执行“ echo $ {array [2]}”。因此,“ unset”不是删除bash数组中元素的正确机制。 (3认同)
  • 您可以通过以下方式更新/刷新索引:`array=(${array[*]})` (3认同)

Nik*_*olm 8

这是一个使用映射文件的单行解决方案:

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
Run Code Online (Sandbox Code Playgroud)

例子:

$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "^Claire\nSmith$")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 5 Contents: Adam Bob David Eve Fred
Run Code Online (Sandbox Code Playgroud)

此方法通过修改/交换 grep 命令提供了极大的灵活性,并且不会在数组中留下任何空字符串。


con*_*tio 5

仅部分回答

删除数组中的第一项

unset 'array[0]'
Run Code Online (Sandbox Code Playgroud)

删除数组中的最后一项

unset 'array[-1]'
Run Code Online (Sandbox Code Playgroud)

  • @jarno:必须使用这些引号:如果当前目录中有一个名为“array0”的文件,那么由于“array[0]”是 glob,因此在 unset 命令之前它将首先扩展到“array0”。 (7认同)

das*_*h-o 5

此答案特定于从大型数组中删除多个值的情况,其中性能很重要。

投票最多的解决方案是 (1) 数组上的模式替换,或 (2) 迭代数组元素。第一个速度很快,但只能处理具有不同前缀的元素,第二个有 O(n*k),n=数组大小,k=要删除的元素。关联数组是相对较新的功能,在最初发布问题时可能并不常见。

对于精确匹配的情况,n 和 k 较大,可以将性能从 O(n k) 提高到 O(n+k log(k))。实际上,O(n) 假设 k 远低于 n。大多数加速是基于使用关联数组来识别要删除的项目。

性能(n 数组大小,要删除的 k 值)。用户时间的性能度量秒

   N     K     New(seconds) Current(seconds)  Speedup
 1000   10     0.005        0.033             6X
10000   10     0.070        0.348             5X
10000   20     0.070        0.656             9X
10000    1     0.043        0.050             -7%
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,current解与 N*K 呈线性关系,而fast解实际上与 K 呈线性关系,但常数要低得多。该fast溶液略慢VS的是current溶液当k = 1时,由于额外设置。

“快速”解决方案:数组=输入列表,删除=要删除的值列表。

        declare -A delk
        for del in "${delete[@]}" ; do delk[$del]=1 ; done
                # Tag items to remove, based on
        for k in "${!array[@]}" ; do
                [ "${delk[${array[$k]}]-}" ] && unset 'array[k]'
        done
                # Compaction
        array=("${array[@]}")
Run Code Online (Sandbox Code Playgroud)

根据current投票最多的答案对解决方案进行基准测试。

    for target in "${delete[@]}"; do
        for i in "${!array[@]}"; do
            if [[ ${array[i]} = $target ]]; then
                unset 'array[i]'
            fi
        done
    done
    array=("${array[@]}")
Run Code Online (Sandbox Code Playgroud)