假设我有一个bash数组(例如所有参数的数组),并希望删除与特定模式匹配的所有参数,或者将所有剩余元素复制到新数组。或者,反过来,保持元素匹配模式。
举例说明:
x=(preffoo bar foo prefbaz baz prefbar)
我希望删除以pref
开头的所有内容以获取
y=(bar foo baz)
(订单不相关)
如果我想要用空格分隔的单词列表,那该怎么办?
x="preffoo bar foo prefbaz baz prefbar"
并再次删除以pref
开头的所有内容以获取
y="bar foo baz"
答案 0 :(得分:10)
剥离扁平字符串的另一种方法是将其转换为数组,然后使用数组方法:
x="preffoo bar foo prefbaz baz prefbar"
x=($x)
x=${x[@]//pref*}
将其与数组的开头和结尾进行对比:
x=(preffoo bar foo prefbaz baz prefbar)
x=(${x[@]//pref*})
答案 1 :(得分:9)
如果考虑包含空格的元素的可能性(更不用说“怪异”字符),过滤数组会很棘手。到目前为止给出的特定答案(指的是${x[@]//pref*/}
的各种形式)将失败,这样的数组。
我已经对此问题进行了一些调查并找到了一个解决方案,但它并不是一个很好的单线程。但至少它是。
为了举例说明,我们假设arr
命名我们想要过滤的数组。我们将从核心表达开始:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
值得一提的元素已经很少了:
"${!ARR[@]}"
计算数组的索引(而不是元素)。"${!ARR[@]}"
是必须的。您不得跳过引号或将@
更改为*
。否则表达式将在关键数组中断开,其中键包含空格(例如)。do
之后的部分可以是您想要的任何内容。这个想法只是你必须对你不希望在数组中拥有的元素进行unset
。-v
和引号unset
,否则可能会发生不好的事情。do
之后的部分符合上述建议,您可以使用&&
或||
来过滤掉通过或失败条件的元素。ARR
第二行,将使用关联数组。 (我没有迅速推出一个通用表达式来处理这两个,而我不需要一个......)。对于普通数组,如果要连续索引,则需要它。因为数组元素上的unset
不会修改(逐个删除)更高索引的元素 - 它只会在索引中形成一个漏洞。现在,如果你只迭代数组(或整个扩展它),这没有问题。但是对于其他情况,您需要重新分配索引。另请注意,如果索引中有任何漏洞,也会将其删除。因此,如果您需要保留现有漏洞,则必须在unset
和最终重新分配旁边完成更多逻辑。现在谈到这个状况。如果您可以使用[[ ]]
表达式,这是一种简单的方法。 (参见here。)特别是它支持使用Extended Regular Expressions进行正则表达式匹配。 (参见here。)如果您希望数组元素不仅包含空格而且还包含新行,请注意使用grep
或任何其他基于行的工具。 (虽然一个非常讨厌的文件名可能会有一个新行字符,我认为......)
参考问题本身,[[ ]]
表达式必须是:
[[ ${ARR[$index]} =~ ^pref ]]
(如上所述&& unset
)
让我们终于看看这对于那些困难的案例是如何运作的。首先我们构造数组:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces")'
ARR+=($'pref\nwith\nnew line')
ARR+=($'\npref with new line before')
通过运行declare -p ARR
并获取:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces" [7]="pref
with
new line" [8]="
pref with new line before")'
现在我们运行过滤器表达式:
for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done
和另一个测试(declare -p ARR
)给出了预期的结果:
declare -a ARR='([1]="bar" [2]="foo" [4]="baz" [8]="
pref with new line before")'
请注意如何删除以pref
开头的所有元素,但索引没有更改。另请注意,${ARRAY[8]}
仍然存在,因为它以新行而不是pref
开头。
现在进行最后的重新分配:
ARR=("${ARR[@]}")
并检查(declare -p ARR
):
declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]="
pref with new line before")'
这正是预期的结果。
收尾笔记。如果可以将其改成灵活的单行程,那就太好了。但我认为有一种方法可以让它更短更简单,因为它现在没有定义功能或类似功能。
至于函数,让它接受数组,返回数组并且易于配置测试以排除或保留它也会很好。但是我现在对Bash不够好。
答案 2 :(得分:7)
要删除扁平字符串(Hulk已经给出了数组的答案),您可以打开extglob
shell选项并运行以下扩展
$ shopt -s extglob
$ unset x
$ x="preffoo bar foo prefbaz baz prefbar"
$ echo ${x//pref*([^ ])?( )}
bar foo baz
extglob
和*(pattern-list)
表单需要?(pattern-list)
选项。这允许您使用正则表达式(尽管与大多数正则表达式的形式不同),而不仅仅是路径名扩展(*?[
)。
Hulk为数组提供的答案仅适用于数组。如果它似乎在扁平字符串上工作,那只是因为在测试中数组没有先取消。
e.g。
$ x=(preffoo bar foo prefbaz baz prefbar)
$ echo ${x[@]//pref*/}
bar foo baz
$ x="preffoo bar foo prefbaz baz prefbar"
$ echo ${x[@]//pref*/}
bar foo baz
$ unset x
$ x="preffoo bar foo prefbaz baz prefbar"
$ echo ${x[@]//pref*/}
$
答案 3 :(得分:6)
你可以这样做:
删除所有出现的子字符串。
# Not specifing a replacement defaults to 'delete' ...
echo ${x[@]//pref*/} # one two three four ve ve
# ^^ # Applied to all elements of the array.
编辑:
对于白色空间,它有点相同
x="preffoo bar foo prefbaz baz prefbar"
echo ${x[@]//pref*/}
输出:
bar foo baz
答案 4 :(得分:1)
我定义并使用了以下功能:
# Removes elements from an array based on a given regex pattern.
# Usage: filter_arr pattern array
# Usage: filter_arr pattern element1 element2 ...
filter_arr() {
arr=($@)
arr=(${arr[@]:1})
dirs=($(for i in ${arr[@]}
do echo $i
done | grep -v $1))
echo ${dirs[@]}
}
使用示例:
$ arr=(chicken egg hen omelette)
$ filter_arr "n$" ${arr[@]}
输出:
鸡蛋煎蛋卷
函数的输出是一个字符串。要将其转换回数组:
$ arr2=(`filter_arr "n$" ${arr[@]}`)
答案 5 :(得分:1)
这是使用grep的一种方式:
()
这里的问题是int[] numbers () [] {
return null;
}
导致setBodyStyle
扩展,并用换行符分隔了各个项目,因此可以通过grep通过管道进行传输。
尤其是,这将处理数组项内部嵌入的空间。