假设我有一个bash数组(例如所有参数的数组),并希望删除与特定模式匹配的所有参数,或者将所有剩余元素复制到新数组.或者,反过来,保持元素匹配模式.
举例说明:
x=(preffoo bar foo prefbaz baz prefbar)
Run Code Online (Sandbox Code Playgroud)
我想删除所有内容pref以便获取
y=(bar foo baz)
Run Code Online (Sandbox Code Playgroud)
(订单不相关)
如果我想要用空格分隔的单词列表,那该怎么办?
x="preffoo bar foo prefbaz baz prefbar"
Run Code Online (Sandbox Code Playgroud)
然后再次删除所有内容pref以便获取
y="bar foo baz"
Run Code Online (Sandbox Code Playgroud)
Ada*_*ura 15
如果考虑包含空格的元素的可能性(更不用说"怪"字符),过滤数组是很棘手的.特别是到目前为止给出的答案(指各种形式${x[@]//pref*/})将会失败.
我已经对这个问题进行了一些调查并找到了解决方案,但它并不是一个很好的单线程.但至少它是.
为了举例说明,我们假设arr名称是我们要过滤的数组.我们将从核心表达开始:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
Run Code Online (Sandbox Code Playgroud)
已经很少有值得一提的元素:
"${!ARR[@]}" 计算数组的索引(而不是元素)."${!ARR[@]}"是必须的.您不得跳过引号或更改@为*.否则表达式将在关键数组中中断,其中键包含空格(例如).do可以是你想要的任何东西.这个想法只是你必须unset按照你不希望在数组中拥有的元素所示.-v和报价,unset否则可能会发生坏事.do如上所述,您可以使用&&或||过滤掉通过或失败条件的元素.ARR,只需要非关联数组,并且会与关联数组分开.(我没有迅速推出一个通用的表达式来处理这两个,而我不需要一个......).对于普通数组,如果要连续索引,则需要它.因为unset在数组元素上不会修改(逐个删除)更高索引的元素 - 它只会在索引中形成一个漏洞.现在,如果你只迭代数组(或整个扩展它),这没有问题.但是对于其他情况,您需要重新分配索引.另请注意,如果索引中有任何漏洞,也会将其删除.因此,如果您需要保留现有漏洞,则必须在unset最终重新分配之外完成更多逻辑.现在谈到这个条件.[[ ]]如果可以使用它,表达式是一种简单的方法.(参见此处.)特别是它支持使用扩展正则表达式进行正则表达式匹配.(请参阅此处.)grep如果您希望数组元素不仅包含空格而且还包含新行,请注意使用或任何其他基于行的工具.(虽然一个非常讨厌的文件名可能有一个新的行字符,我认为...)
提到问题本身,[[ ]]表达必须是:
[[ ${ARR[$index]} =~ ^pref ]]
Run Code Online (Sandbox Code Playgroud)
(&& 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')
Run Code Online (Sandbox Code Playgroud)
通过运行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")'
Run Code Online (Sandbox Code Playgroud)
现在我们运行过滤器表达式:
for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done
Run Code Online (Sandbox Code Playgroud)
另一个测试(declare -p ARR)给出了预期的:
declare -a ARR='([1]="bar" [2]="foo" [4]="baz" [8]="
pref with new line before")'
Run Code Online (Sandbox Code Playgroud)
注意如何pref删除所有以元素开头的元素,但索引没有改变.还要注意,${ARRAY[8]}它仍然存在,因为它以新行开始而不是pref.
现在进行最后的重新分配:
ARR=("${ARR[@]}")
Run Code Online (Sandbox Code Playgroud)
并检查(declare -p ARR):
declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]="
pref with new line before")'
Run Code Online (Sandbox Code Playgroud)
这正是预期的结果.
截止日期.如果可以将其改成灵活的单行程,那就太好了.但是我认为没有办法让它变得更短更简单,因为它现在没有定义功能或类似功能.
至于函数,让它接受数组,返回数组并且易于配置测试以排除或保留它也会很好.但是我现在对Bash不够好.
Pau*_*ce. 10
剥离扁平字符串的另一种方法是将其转换为数组,然后使用数组方法:
x="preffoo bar foo prefbaz baz prefbar"
x=($x)
x=${x[@]//pref*}
Run Code Online (Sandbox Code Playgroud)
将其与数组的开头和结尾进行对比:
x=(preffoo bar foo prefbaz baz prefbar)
x=(${x[@]//pref*})
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
删除所有出现的子字符串.
# Not specifing a replacement defaults to 'delete' ...
echo ${x[@]//pref*/} # one two three four ve ve
# ^^ # Applied to all elements of the array.
Run Code Online (Sandbox Code Playgroud)
编辑:
对于白色空间,它有点相同
x="preffoo bar foo prefbaz baz prefbar"
echo ${x[@]//pref*/}
Run Code Online (Sandbox Code Playgroud)
输出:
酒吧foo baz
要剥离扁平字符串(Hulk已经给出了数组的答案),您可以打开extglobshell选项并运行以下扩展
$ shopt -s extglob
$ unset x
$ x="preffoo bar foo prefbaz baz prefbar"
$ echo ${x//pref*([^ ])?( )}
bar foo baz
Run Code Online (Sandbox Code Playgroud)
和表单extglob需要该选项.这允许您使用正则表达式(尽管与大多数正则表达式的形式不同),而不仅仅是路径名扩展().*(pattern-list)?(pattern-list)*?[
Hulk为数组提供的答案仅适用于数组.如果它似乎在扁平字符串上工作,那只是因为在测试数组时没有先取消.
例如
$ 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*/}
$
Run Code Online (Sandbox Code Playgroud)