通过内容(存储在变量中)从bash数组中删除元素而不留空白插槽

时间:2014-05-04 23:44:29

标签: arrays bash unset

我在bash脚本中有一个数组list,还有一个变量var。我知道$var中出现${list[@]},但没有简单的方法来确定其索引。我想将其从list删除。

This回答达到了我所需要的非常接近的效果,除了list保留$var曾经存在的空元素。注意,例如:

$ list=(one two three)
$ var="two"
$ list=( "${list[@]/$var}" )
$ echo ${list[@]}
one three
$ echo ${#list[@]}
3

如果我在第三行使用delete=( "$var" )并将$var替换为$delete,则会发生同样的情况。此外,做list=( "${list[@]/$var/}" )也没有任何区别。 (我注意到,尝试对该答案的评论,我设法使用list=( "${list[@]/%$var}" )仅匹配整个单词,省略#。)

我还看到this回答提出了一个很好的技巧来跟踪索引并使用unset,但在我的情况下这是不可行的。最后,同样的问题也出现here,除了OP对结果感到满意,并且可能没有遇到空元素稍后在我的脚本中为我创建的问题,当我迭代{{1 }}。我尝试通过如下使用扩展来否定该问题,没有任何明显的影响:

list

当我for item in "${list[@]}"; do if [ -n ${item:+'x'} ];then ... fi done 时,情况也是一样的,而且我的想法已经不多了。建议?

修改

我不明白为什么会这样,但@ l0b0的评论让我注意到了一些事情。使用上面的序言,我得到:

[ ${#item} > 0 ]

但:

$ for item in "${list[@]}"; do echo "Here!"; done
Here!
Here!
Here!

我不确定我可以在脚本中省略引号,因为项目相当复杂(文件名和路径都包含空格和奇数字符)。

3 个答案:

答案 0 :(得分:5)

您可以从现有数组中删除元素,但整个过程不是很简单,可能看起来像黑客。

#!/bin/bash

list=( "one" "two" "three" "four" "five" )
var1="two"
var2="four"

printf "%s\n" "Before:"
for (( i=0; i<${#list[@]}; i++ )); do 
    printf "%s = %s\n" "$i" "${list[i]}"; 
done

for (( i=0; i<${#list[@]}; i++ )); do 
    if [[ ${list[i]} == $var1 || ${list[i]} == $var2 ]]; then
        list=( "${list[@]:0:$i}" "${list[@]:$((i + 1))}" )
        i=$((i - 1))
    fi
done

printf "\n%s\n" "After:"
for (( i=0; i<${#list[@]}; i++ )); do 
    printf "%s = %s\n" "$i" "${list[i]}"; 
done

此脚本输出:

Before:
0 = one
1 = two
2 = three
3 = four
4 = five

After:
0 = one
1 = three
2 = five

脚本的关键部分是:

list=( "${list[@]:0:$i}" "${list[@]:$((i + 1))}" )

这里我们通过指定索引和长度来重新构造现有数组,以完全从数组中删除元素并重新排序索引。

答案 1 :(得分:4)

如果你想删除数组元素&amp;移动索引,你可以使用l0b0或JS웃的答案。

但是,如果您不想转移索引,可以使用下面的script-let: (特别适用于关联数组)

$ list=(one two three)
$ delete_me=two
$ for i in ${!list[@]};do
    if [ "${list[$i]}" == "$delete_me" ]; then
        unset list[$i]
    fi 
done

$ for i in ${!list[@]};do echo $i = ${list[$i]}; done
0 = one
2 = three

答案 2 :(得分:1)

如果你想按值删除并移动索引,我认为你必须创建一个新数组:

list=(one two three)
new_list=() # Not strictly necessary, but added for clarity
var="two"
for item in ${list[@]}
do
    if [ "$item" != "$var" ]
    then
        new_list+=("$item")
    fi
done
list=("${new_list[@]}")
unset new_list

测试:

$ echo "${list[@]}"
one three
$ echo "${#list[@]}"
2