I need to remove an element from an array in bash shell. Generally I'd simply do:
array=("${(@)array:#<element to remove>}")
Unfortunately the element I want to remove is a variable so I can't use the previous command. Down here an example:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Any idea?
POSIX shell script does not have arrays.
So most probably you are using a specific dialect such as
bash
, korn shells orzsh
.Therefore, your question as of now cannot be answered.
Maybe this works for you:
Here's a (probably very bash-specific) little function involving bash variable indirection and
unset
; it's a general solution that does not involve text substitution or discarding empty elements and has no problems with quoting/whitespace etc.Use it like
delete_ary_elmt ELEMENT ARRAYNAME
without any$
sigil. Switch the== $word
for== $word*
for prefix matches; use${elmt,,} == ${word,,}
for case-insensitive matches; etc., whatever bash[[
supports.It works by determining the indices of the input array and iterating over them backwards (so deleting elements doesn't screw up iteration order). To get the indices you need to access the input array by name, which can be done via bash variable indirection
x=1; varname=x; echo ${!varname} # prints "1"
.You can't access arrays by name like
aryname=a; echo "${$aryname[@]}
, this gives you an error. You can't doaryname=a; echo "${!aryname[@]}"
, this gives you the indices of the variablearyname
(although it is not an array). What DOES work isaryref="a[@]"; echo "${!aryref}"
, which will print the elements of the arraya
, preserving shell-word quoting and whitespace exactly likeecho "${a[@]}"
. But this only works for printing the elements of an array, not for printing its length or indices (aryref="!a[@]"
oraryref="#a[@]"
or"${!!aryref}"
or"${#!aryref}"
, they all fail).So I copy the original array by its name via bash indirection and get the indices from the copy. To iterate over the indices in reverse I use a C-style for loop. I could also do it by accessing the indices via
${!arycopy[@]}
and reversing them withtac
, which is acat
that turns around the input line order.A function solution without variable indirection would probably have to involve
eval
, which may or may not be safe to use in that situation (I can't tell).To expand on the above answers, the following can be used to remove multiple elements from an array, without partial matching:
This will result in an array containing: (two onetwo three threefour "one six")
Using
unset
To remove an element at particular index, we can use
unset
and then do copy to another array. Only justunset
is not required in this case. Becauseunset
does not remove the element it just sets null string to the particular index in array.Output is
Using
:<idx>
We can remove some set of elements using
:<idx>
also. For example if we want to remove 1st element we can use:1
as mentioned below.Output is
The following works as you would like in
bash
andzsh
:If need to delete more than one element:
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.Note that if you do this, and one or more elements is removed, the indices will no longer be a continuous sequence of integers.
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:
What I do is:
BAM, that item is removed.