首页 > 解决方案 > 将区间表达式与 bash 扩展通配符一起使用

问题描述

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

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

实际问题

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

给定一些示例字符串,例如

foobar0000
foobar00
foobar000

应该产生

foobar00
foobar
foobar0

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

x='foobar000'

分别。我尝试使用如下的区间表达式,这对我来说似乎很明显它不起作用

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

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

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

标签: bashshell

解决方案


不知何故,我设法在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
    

    或者

    $ 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次出现和mn次可能出现。这些可以使用 ksh-globs 构建@(pat) n次和?(pat) mn次。对于n=2m=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)可用于任何可以使用#or##运算符的地方,除了在表达式(*/)#(*/)##文件名生成中/具有特殊含义的地方;它不能与其他 globbing 标志结合使用,如果放错位置,则会发生错误的模式错误。相当于{N,M}正则表达式中的形式。前一个字符或组需要在N和之间匹配M,包括在内。表格 (#cN)要求完全N匹配;(#c,M)相当于指定N0; (#cN,)指定对匹配数没有最大限制。

$ echo ${var%%(0|1)(#c2,3)}

推荐阅读