bash: Remove variable from array? [duplicate]

2019-05-16 23:31发布

This question already has an answer here:

#!/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?

标签: bash shell
3条回答
戒情不戒烟
2楼-- · 2019-05-17 00:04

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
查看更多
小情绪 Triste *
3楼-- · 2019-05-17 00:05

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
查看更多
叛逆
4楼-- · 2019-05-17 00:07

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}/} )
查看更多
登录 后发表回答