如果考虑到数组中可能包含空格(更不用说“更奇怪”的字符了),筛选一个数组就会变得棘手。特别是迄今为止给出的答案(涉及各种形式的${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
和最终重新赋值之外进行更多的逻辑处理。
现在,就条件而言,如果您可以使用它,那么 [[ ]]
表达式是一种容易的方式。(请参见这里)。特别是它支持使用 扩展正则表达式 进行匹配。(请参见这里)。此外,请小心使用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 不太熟悉,暂时做不到。
unset -v 'ARR[$index]'
。单引号不会阻止$index
的替换吗? - Roland Webera=(0 1 "and two"); for i in ${!a[*]}; do echo "$i=${a[$i]}"; ((i=1)) && unset -v 'a[$i]'; done; echo; declare -p a; a=("${a[@]}"); declare -p a
- mivk