I would like to make a function that takes a bash array like this one:
a=("element zero" "element one" "element two")
and removes one element like "element one" and leaves a the array like this:
a=("element zero" "element two")
such that echo $a[1] will print out element two and not zero.
I've seen several attempts at this, but haven't found one that did it cleanly or without breaking elements that have spaces into multiple elements. Or just setting the element to be blank (i.e. not shifting the indexes of subsequent array elements).
# 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
...now, for a function, if you have bash 4.3, you can use namevars to do this without any eval at all:
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
}
For older versions of bash, it's a bit stickier:
remove() {
local cmd
unset "$1[$2]"
printf -v cmd '%q=( "${%q[@]}" )' "$1" "$1" && eval "$cmd"
}
The use of printf with %q format strings is a bit of paranoia -- it makes it harder for maliciously chosen values (in this case, variable names) to perform actions of their choice, as opposed to simply failing with no effect.
All that said -- it's better if you don't renumber your arrays. If you leave off the renumbering step, such that after deleting entry a[1] you simply have a sparse array with no content at that index (which is different from an empty string at that index -- bash "arrays" are actually stored as linked lists or hash tables [in the associative case], not as arrays at all, so sparse arrays are memory-efficient), the delete operation is much faster.
This doesn't break your ability to iterate over your arrays if you retrieve ask the array for its keys rather than supplying them externally, as in:
for key in "${!a[@]}"; do
value="${a[$key]}"
echo "Entry $key has value $value"
done
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With