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

时间:2018-05-31 18:27:36

标签: bash shell

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

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

实际问题

我在今天的一个问题中遇到了一个要求,即只删除一个字符串中的一对尾随零。尝试使用bash

中的扩展glob支持来解决这个问题

给出一些像

这样的示例字符串
foobar0000
foobar00
foobar000

应该产生

foobar00
foobar
foobar0

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

x='foobar000'

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

echo ${x%%+([0]{2})}

即。在ERE中使用sed作为sed -E 's/[0]{2}$//'或在BRE中使用sed 's/[0]\{2\}$//'

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

2 个答案:

答案 0 :(得分:3)

不知怎的,我设法在bash的限制范围内找到了解决方法。

是否在bash中实现了interval glob-expression?

没有!与其他shell(如ksh和zsh)相比,bash没有实现globbing的区间表达式。

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

是的!但是,它实际上并不实用,有时可以通过printf获益。我们的想法是使用KSH-globs {m,n}@(pattern)构建模仿?(pattern)区间的球形表达式。

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

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

    我们的想法是重复n次模式。对于大型 n ,您可以使用printf

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)}
    foobar000
    

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

    与以前相同,但附加*(pattern)

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)*(0|1)}
    foobar
    

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

    区间表达式{n,m}意味着我们肯定有 n 外观和 m-n 可能的外观。这些可以使用ksh-globs @(pat) n 次和?(pat) m-n 次构建。对于 n = 2 m = 3 ,这会导致:

    $ var="foobar01010"
    $ echo ${var%%@(0|1)@(0|1)?(0|1)}
    foobar010
    

    $ 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
    

    构造区间表达式{n,m}的另一种方法是使用ksh-glob 除了!(pat)之外的任何模式,它允许我们说:给我所有,除了。 ..

      

    <强> man bash   !(pattern-list):匹配除了某个给定模式之外的任何内容

    这样我们就可以写

    $ echo ${var%%!(!(*$p)|@$p@$p@$p+$p|?$p)}
    

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

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

其他炮弹怎么样?

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)}

ZSH

zsh也有一种区间表达形式。它是一个globbing标志,它是EXTENDED_GLOB选项的一部分:

  

<强> man zshall

     

(#cN,M)可以在(#cN,M)#运算符可以使用的任何位置使用标记##,但表达式(*/)#和{{1}除外在文件名生成中,(*/)##具有特殊含义;它不能与其他globbing标志组合使用,如果放错位置会发生错误的模式错误。它等同于正则表达式中的/形式。上一个字符或组必须在{N,M}N次之间进行匹配。表格                 M完全需要(#cN)次匹配; N相当于将(#c,M)指定为N; 0指定匹配数量没有最大限制。

(#cN,)

答案 1 :(得分:1)

  • 没有
  • &#34;扩展模式匹配功能&#34;使用extglob启用(因此我们称之为扩展的glob)。扩展模式匹配功能用于名为pattern matching的操作中。使用[[...]]=运算符时,filename expansion!= conditional constructs使用模式匹配。文件名扩展用于参数扩展。
  • 正如您在pattern matching中所看到的,扩展的glob或不是,模式匹配不支持像[set]{count}这样的表达式。例如,我们可以使用+(..)匹配一个或多个匹配项,依此类推,但指定模式的出现次数是不可能的。
  • 但这是bash,而bash是强大的。我们可以通过重复模式来指定模式的出现次数。我们无法指定结尾或开头(我的意思是在正则表达式中使用^$),但我们可以使用${parameter%%word}参数扩展来删除参数的尾部。所以这将有效:

var='foobar000'
echo ${var%%[0][0]}

并且,通过一些简单的黑客行为,我们可以做到这一点:

var='foobar000'
echo ${var%%$(yes '[0]' | head -n 2 | tr -d '\n')}

这将从字符串中删除两个尾随零。