Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove an element from a Bash array

People also ask

How do you delete an element from an array in Bash?

To really remove an exact item, you need to walk through the array, comparing the target to each element, and using unset to delete an exact match. Note that if you do this, and one or more elements is removed, the indices will no longer be a continuous sequence of integers.

How do I remove a specific element from an array?

pop() function: This method is use to remove elements from the end of an array. shift() function: This method is use to remove elements from the start of an array. splice() function: This method is use to remove elements from the specific index of an array.

How do I remove an item from an array index?

Find the index of the array element you want to remove using indexOf , and then remove that index with splice . The splice() method changes the contents of an array by removing existing elements and/or adding new elements. The second parameter of splice is the number of elements to remove.

What is the syntax to remove an element from a specific array index in Linux?

One of the methods is “unset,” which is used to delete an element from a specific index and afterward replace it with some other array. Several other sets of elements can be deleted using: also. You can remove the list element from the end but only the solitary one using the pop() method.


The following works as you would like in bash and zsh:

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

If need to delete more than one element:

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

Caveat

This technique actually removes prefixes matching $delete from the elements, not necessarily whole elements.

Update

To really remove an exact item, you need to walk through the array, comparing the target to each element, and using unset to delete an exact match.

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

Note that if you do this, and one or more elements is removed, the indices will no longer be a continuous sequence of integers.

$ declare -p array
declare -a array=([0]="pluto" [2]="bob")

The simple fact is, arrays were not designed for use as mutable data structures. They are primarily used for storing lists of items in a single variable without needing to waste a character as a delimiter (e.g., to store a list of strings which can contain whitespace).

If gaps are a problem, then you need to rebuild the array to fill the gaps:

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

You could build up a new array without the undesired element, then assign it back to the old array. This works in bash:

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

This yields:

echo "${array[@]}"
pippo

This is the most direct way to unset a value if you know it's position.

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

This answer is specific to the case of deleting multiple values from large arrays, where performance is important.

The most voted solutions are (1) pattern substitution on an array, or (2) iterating over the array elements. The first is fast, but can only deal with elements that have distinct prefix, the second has O(n*k), n=array size, k=elements to remove. Associative array are relative new feature, and might not have been common when the question was originally posted.

For the exact match case, with large n and k, possible to improve performance from O(nk) to O(n+klog(k)). In practice, O(n) assuming k much lower than n. Most of the speed up is based on using associative array to identify items to be removed.

Performance (n-array size, k-values to delete). Performance measure seconds of user time

   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%

As expected, the current solution is linear to N*K, and the fast solution is practically linear to K, with much lower constant. The fast solution is slightly slower vs the current solution when k=1, due to additional setup.

The 'Fast' solution: array=list of input, delete=list of values to remove.

        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[@]}")

Benchmarked against current solution, from the most-voted answer.

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

Here's a one-line solution with mapfile:

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

Example:

$ 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

This method allows for great flexibility by modifying/exchanging the grep command and doesn't leave any empty strings in the array.