Bash数组删除包含特定字符串的元素

4

我简要说明,我所拥有的是

array=( one.a two.b tree.c four.b five_b_abc)

I want this

array=( two.b four.b five_b_abc )

我从这里找到了以下内容:

# replace any array item matching "b*" with "foo"
array=( foo bar baz )
array=( "${array[@]/%b*/foo}" )
echo "${orig[@]}"$'\n'"${array[@]}"

然而,这并不起作用。

array2=( ${array[@]//%^.p/})

结果 array2=array

这将删除所有内容中的p

array2=(${array[@]/*p*/})

结果 array2=( one.a tree.c )

我需要一个关于如何添加^p(除了p之外的所有内容),并得出我的解决方案的想法。

array2=(${array[@]/*^p*/}

这是一个相当大的数组,约有10k个元素,需要对其进行操作。鉴于数据量大,请尽可能使用高效的解决方案,不要采用循环方式。

4个回答

6

编辑:添加了时间比较(在结尾处)并且去掉了tr

使用bash的参数扩展,即${var[@]....}可以替换数组元素的内容,但你不能真正地删除元素。最好的方法是使用参数扩展使你不需要的元素变为空("")。

相反地,你可以使用printfsedIFS。这样做有一个优点,允许你使用完整的正则表达式语法(而不仅仅是shell通配符表达式),同时比使用循环快得多。

这个例子会保留包含c的数组元素。
注意:此方法适用于数据中包含空格的情况,这是通过IFS=\n实现的。

IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/c/!d'))

以下是带有before/after dumps的内容:

#!/bin/bash

a=(one.ac two.b tree.c four.b "five b abcdefg" )

echo "======== Original array ===="
printf '%s\n' "${a[@]}"

echo "======== Array containing only the matched elements 'c' ===="
IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/c/!d'))
printf '%s\n' "${a[@]}"
echo "========"

输出:

======== Original array ====
one.ac
two.b
tree.c
four.b
five b abcdefg
======== Array containing only the matched elements 'c' ====
one.ac
tree.c
five b abcdefg
========

一般参考:测试包含10k个元素的数组。选择其中5k个:
a=( a\ \ \ \ b{0..9999} )

printf方法耗时:0m0.226s(结果为顺序索引值)
    第一种循环方法:0m4.007s(它在索引值中留下空隙)
第二种循环方法:0m7.862s(结果为顺序索引值)

printf方法:

IFS=$'\n'; a=($(printf '%s\n' "${a[@]}" |sed '/.*[5-9]...$/!d'))

第一种循环方法:

iz=${#a[@]}; j=0
for ((i=0; i<iz; i++)) ;do
    [[ ! "${a[i]}" =~ .*[5-9]...$ ]] && unset a[$i]
done

第二种循环方法:
iz=${#a[@]}; j=0
for ((i=0; i<iz; i++)) ;do
    if [[ ! "${a[i]}" =~ .*[5-9]...$ ]] ;then
        unset a[$i]
    else 
        a[$j]="${a[i]}=$i=$j"; ((j!=i)) && unset a[$i]; ((j+=1)); 
    fi
done

我也想感谢你。从现在开始,你添加的网站已经被加入了我的书签。 - nkvnkv

5
您可以尝试这样做:
array2=(`echo ${array[@]} | sed 's/ /\n/g' | grep b`)

两种解决方案都很好,但我使用了你的。 - nkvnkv

0

我知道这是一个bash问题,但我使用了一个Python脚本将其变成了一行代码,使其易于阅读。

$ array=( one.a two.b tree.c four.b five_b_abc)
$ printf '%s\n' "${array[@]}" | python3 -c "import sys; print(' '.join([ s.strip() for s in sys.stdin if 'b' in s ]));"
two.b four.b five_b_abc

0
如果您将IFS设置为$'\n',则可以使用带有'*'作为下标的echo命令来处理数组,而不是使用带有'@'的printf命令:
IFS=$'\n'; a=($(echo "${a[*]}" | sed '/.*[5-9]...$/!d'))

'*'下标将数组元素连接在一起,由IFS分隔。(顺便说一句,我最近才学会这个。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接