代码之家  ›  专栏  ›  技术社区  ›  Inian

在bash扩展全局搜索中使用区间表达式

  •  6
  • Inian  · 技术社区  · 6 年前

    我知道事实上 bash 支持扩展glob,并支持 @(foo|bar) , *(foo) ?(foo) . 这种语法是非常独特的,即不同于ERES的语法——扩展的globs使用前缀表示法(在这里出现运算符 before 它的操作数),而不是像eres那样的后缀。

    我想知道它是否支持类型为 {n,m} 即,如果大括号中有一个数字,则重复前面的regexp n 如果有两个数字用逗号分隔,则重复前面的regexp n m 时代。我找不到一个特别的文档表明在extended glob中启用了这种支持。

    实际问题

    我在今天的一个问题中遇到一个要求,只删除字符串中的一对尾随零。尝试使用中的扩展glob支持解决此问题 猛击

    给定一些示例字符串,如

    foobar0000
    foobar00
    foobar000
    

    应该产生

    foobar00
    foobar
    foobar0
    

    我试着用参数扩展的扩展glob来做

    x='foobar000'
    

    分别是。我试着用下面的区间表达式,这在我看来显然是行不通的

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

    即类似使用 sed 作为 sed -E 's/[0]{2}$//' 或者在布雷斯 sed 's/[0]\{2\}$//'

    所以我的问题是,这有可能使用任何扩展的glob运算符吗?我正在寻找特定于在中使用扩展glob支持的答案 猛击 如果不可能的话也会接受“不”。

    2 回复  |  直到 6 年前
        1
  •  3
  •   kvantour    6 年前

    不知怎的,我设法在 bash .

    interval glob表达式是在bash中实现的吗?

    不!与其他shell(如ksh和zsh)相比,bash没有实现用于全局处理的间隔表达式。

    我们可以在bash中模拟间隔表达式吗?

    对!然而,它并不实际,有时可以通过使用 printf . 我们的想法是建立一个球形的表达来模仿 {m,n} 使用ksh globs的间隔 @(pattern) ?(pattern) .

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

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

      想法是重复这个模式 n 时代。大的 n 你可以用 普林特

      $ 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
      
    • 匹配源 n 给定模式的出现次数( {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)}
      

      注: 你需要在这里做双重排除,因为 ( | )在模式列表中。

    其他炮弹呢?

    KSH96

    区间表达式 {n,m } 已在执行 ksh93 :

    man ksh :

    • {n}(pattern-list) 比赛 n 给定模式的出现次数。
    • {m,n}(pattern-list) 匹配源 n 给定模式的出现次数。如果 被省略, 0 将被使用。如果 n 至少被省略了 匹配出现次数。
    $ echo ${var%%{2,3}(0|1)}
    

    ZSH

    阿尔索 zsh 具有间隔表达式的形式。这是一面环球旗,它是 EXTENDED_GLOB 选项:

    man zshall :

    (#cN,M) 旗帜 (中国大陆,米) 可用于任何 # ## 除了在表达式中之外,可以使用运算符 (*/)# (*/)## 在文件名生成中,其中 / 具有特殊意义;它不能与其他全局标志组合,如果错误放置,则会发生错误的模式错误。它等同于形式 {N,M} 在正则表达式中。前一个字符或组必须匹配 N M 时间,包括在内。形式 (#cN) 完全需要 n 比赛; (#c,M) 相当于指定 n 作为 ; (#cN,) 指定对匹配数没有最大限制。

    $ echo ${var%%(0|1)(#c2,3)}
    
        2
  •  1
  •   KamilCuk    6 年前
    • “扩展模式匹配特性”是使用extglob启用的(因此我们称之为扩展glob)。扩展的模式匹配特性用于名为 pattern matching . 模式匹配用于 filename expansion 而在 [[...]] conditional constructs 使用时 = != 操作员。文件名扩展用于参数扩展。
    • 如你所见 模式匹配 ,是否扩展全局,模式匹配不支持以下表达式 [set]{count} . 例如,我们可以将一个或多个事件与 +(..) 等等,但无法指定模式的出现次数。
    • 但这是巴什,巴什是强大的。我们可以通过重复模式来指定模式的出现次数。我们不能指定结束或开始(我的意思是 ^ $ 在regex中),但是我们可以使用 ${parameter%%word} 参数展开以删除参数的尾随部分。所以这是可行的:

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

    通过一些简单的黑客攻击,我们可以做到:

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

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