This question already has an answer here:
-
Remove an element from a Bash array
17 answers
#!/bin/bash
tank=(one two three)
x=two
unset tank[${x}]
echo ${tank[*]}
I want to remove x from the array but somehow it removes the first element of the array. How can I fix that?
EDIT user2997549 beware: altought the solution seems to works in this case, it is in general wrong. The following command executes a pattern replacement for all elements in the array, which actually works in your example only because you completely control the contents of the array. PLease accept chepner answer which is more general and addresses other issues, so I can then delete my answer.
To completely remove the element by value (not by index), you want a new array like in:
newtank=( ${tank[@]/two/} )
if you just unset an index, the hole will still be in the array; or if you can get rid of the old array
tank=( ${tank[@]/${x}/} )
You have an indexed array, so the values in the [...]
are treated as arithmetic expressions to generate an integer index. Strings in such an expressions are assumed to be parameter names, with undefined parameters evaluating to zero. Since two
is undefined, your attempt is evaluated as
unset tank[${x}] -> unset tank[two] -> unset tank[0]
To safely remove an item from an array, you'll need to walk through the array, copying non-matching items to a new array, then assigning the new array back to the old name. This protects against splitting up array elements that might contain whitespace.
x=two
new_tank=()
for i in "${tank[@]}"; do
if [[ $i != $x ]]; then
new_tank+=("$i")
fi
done
tank=( "${new_tank[@]}" )
More succinctly, as pointed out by gniourf_gniourf:
for i in "${!tank[@]}"; do
[[ ${tank[i]} = $x ]] && unset tank[i]
done
Depending on what your application is, you may want to consider an associative array instead.
declare -A tank
tank=([one]=1 [two]=2 [three]=3) # Using the keys as the actually elements
x=two
unset tank[$x]
# Prove that two is really gone, with no hole left behind.
for i in "${!tank[@]}"; do
echo "$i"
done
Since there is no way to match elements exactly, as with the regex ^two$
, the only way I see is to rebuild a new array, excluding the unwanted element using test
or [[
to perform exact matching:
tank=( first two twot twoot twooot )
unset work
x=two
for i in ${tank[@]}; do [[ $i = $x ]] || work+=($i); done
tank=$work