使用带有bash扩展globbing的Interval表达式

Ini*_*ian 6 bash shell

我知道一个事实,它bash支持带有正则表达式的扩展glob,如支持@(foo|bar),*(foo)?(foo).这种语法非常独特,即与ERE不同 - 扩展的globs使用前缀表示法(操作符出现before其操作数),而不是像ERE那样的后缀.

我想知道它支持类型的间隔表达式特征{n,m},即如果在大括号一个数,如前述的正则表达式被重复n时间或是否存在由逗号分隔的两个数字,前述正则表达式被重复nm次.我找不到一个特定的文档,表明在扩展的glob中启用了这种支持.

实际问题

我在今天的一个问题中遇到了一个要求,即只删除字符串中的一对尾随零.尝试使用扩展的glob支持来解决这个问题bash

给出一些样本字符串

foobar0000
foobar00
foobar000
Run Code Online (Sandbox Code Playgroud)

应该产生

foobar00
foobar
foobar0
Run Code Online (Sandbox Code Playgroud)

我尝试使用带参数扩展的扩展glob来做

x='foobar000'
Run Code Online (Sandbox Code Playgroud)

分别.我尝试使用下面的区间表达式,这对我来说显而易见,它不会起作用

echo ${x%%+([0]{2})}
Run Code Online (Sandbox Code Playgroud)

即类似于sed在ERE中使用sed -E 's/[0]{2}$//'或在BRE中使用sed 's/[0]\{2\}$//'

所以我的问题是,这是否可以使用任何扩展的glob运算符?我正在寻找特定于使用扩展的glob支持的答案,bash如果不可能也会采取'否'.

kva*_*our 4

不知何故,我设法找到了一种在bash.

bash 中是否实现了间隔全局表达式?

不!与 ksh 和 zsh 等其他 shell 相比,bash 没有实现用于通配符的区间表达式。

我们可以模仿 bash 中的区间表达式吗?

是的!然而,它并不实用,有时可以通过使用printf. {m,n}这个想法是使用 KSH-globs@(pattern)和来构建模拟间隔的球状表达式?(pattern)

在下面的解释中,我们假设模式存储在变量中p

  • 匹配n给定模式 ( {n}) 的出现次数:

    这个想法是重复模式n次数。对于大n你可以使用printf

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)}
    foobar000
    
    Run Code Online (Sandbox Code Playgroud)

    或者

    $ var="foobar01010"
    $ p=$(printf "@(0|1)%.0s" {1..4})
    $ echo ${var%%$p}
    foobar0
    
    Run Code Online (Sandbox Code Playgroud)
  • 至少匹配m给定模式 ( {m,}) 的出现次数:

    与之前相同,但多了一个*(pattern)

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)*(0|1)}
    foobar
    
    Run Code Online (Sandbox Code Playgroud)

    或者

    $ var="foobar01010"
    $ p="(0|1)"
    $ q=$(printf "@$p%.0s" {1..4})
    $ echo ${var%%$q*$p}
    foobar
    
    Run Code Online (Sandbox Code Playgroud)
  • 匹配给nm模式 ( {m,n}) 的出现次数:

    区间表达式{n,m}意味着我们确实有n种出现和mn种可能的出现。这些可以使用 ksh-glob @(pat) n次和?(pat) mn次来构建。对于n=2m=3,这导致:

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)?(0|1)}
    foobar010
    
    Run Code Online (Sandbox Code Playgroud)

    或者

    $ p="(0|1)"
    $ q=$(printf "@$p%.0s" {1..n})$(printf "?$p%.0s" {n+1..m})
    $ echo ${var%%$q}
    foobar010
    $ var="foobar00200"
    foobar002
    $ var="foobar00020"
    foobar00020
    
    Run Code Online (Sandbox Code Playgroud)

    构造区间表达式的另一种方法{n,m}是使用 ksh-glob除了模式之外的任何内容,这样!(pat)我们就可以说:给我所有,除了......

    man bash: !(pattern-list) : 匹配除给定模式之一之外的任何内容

    这样我们就可以写

    $ echo ${var%%!(!(*$p)|@$p@$p@$p+$p|?$p)}
    
    Run Code Online (Sandbox Code Playgroud)

    或者

    $ p="(0|1)"
    $ pn=$(printf "@$p%.0s" {1..n})
    $ pm=$(printf "?$p%.0s" {1..m-1})
    $ echo ${var%%!(!(*$p)|$pn+$p|$pm)}
    
    Run Code Online (Sandbox Code Playgroud)

    注意:由于模式列表中存在( ) ,因此需要在此处进行双重排除。|

那么其他的贝壳呢?

KSH93

区间表达式{n,m}已在中实现ksh93

man ksh

  • {n}(pattern-list) 匹配n给定模式的出现。
  • {m,n}(pattern-list)匹配mn定模式的出现次数。如果m省略,0将被使用。如果n省略,至少m会匹配出现的次数。
$ echo ${var%%{2,3}(0|1)}
Run Code Online (Sandbox Code Playgroud)

中兴

zsh有区间表达式的形式。它是一个全局标志,是选项的一部分EXTENDED_GLOB

man zshall

(#cN,M) 该标志(#cN,M)可以在任何可以使用#or##运算符的地方使用,但在表达式(*/)#(*/)##文件名生成中除外,其中/具有特殊含义;它不能与其他通配标志组合,如果放错位置,则会出现错误模式错误。相当于{N,M}正则表达式中的形式。前一个字符或组需要在N和时间之间匹配M(包括 和 时间)。表格 (#cN)要求完全N匹配;(#c,M)相当于指定N0; (#cN,)指定匹配数量没有最大限制。

$ echo ${var%%(0|1)(#c2,3)}
Run Code Online (Sandbox Code Playgroud)