如果考虑到元素可能包含空格(更不用说“更奇怪”的字符),过滤数组会很棘手。特别是到目前为止给出的答案(指各种形式的${x[@]//pref*/})对于此类数组将失败。
我已经对这个问题进行了一些调查并找到了解决方案,但它不是一个很好的单线。但至少是这样。
为了举例说明,我们假设ARR 命名我们要过滤的数组。我们将从核心表达式开始:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
已经有几个元素值得一提:
-
"${!ARR[@]}" 计算数组的索引(而不是元素)。
-
"${!ARR[@]}" 表格是必须的。您不得跳过引号或将@ 更改为*。否则表达式将在键包含空格的关联数组上中断(例如)。
-
do 之后的部分可以是任何你想要的。这个想法只是你必须为你不想在数组中包含的元素执行unset。
-
It is advised or even needed 使用
-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 还不够好。