Bash posix 正则表达式可选组

Bash posix 正则表达式可选组

试图从表单类别/名称版本中的字符串中匹配一些 Gentoo 包命名组件,由于其复杂性,我得出了这样的结论:

if [[ "$1" =~ ^([<>]?=?)(([^\/]+)\/)?([^[:space:]]+)-(([[:digit:]]+)?(\.([[:digit:]]+))*([a-z])?(_(alpha|beta|pre|rc|p)([[:digit:]]*))*(-(r([[:digit:]]+))?)?)?$ ]]; then
    # use "${BASH_REMATCH[n]}" here to capture groups contents
fi

它按照预期分割字符串<category/package-name-12345.25b_rc10-r7

Version specifier: <
Category: category
Package name: package-name
Version: 12345.25b_rc10-r7
Version, major: 12345
Version, minor: 25
Version, letter: b
Version, patch type: rc
Version, patch level: 10
Version, revision number: 7

现在,我需要匹配和分割可能缺少版本的字符串,例如category/package-name.

那么,有没有办法让上面的版本部分可选呢?

以上,这部分内容如下:

-(([[:digit:]]+)?(\.([[:digit:]]+))*([a-z])?(_(alpha|beta|pre|rc|p)([[:digit:]]*))*(-(r([[:digit:]]+))?)?)?

我尝试将其更改为:

(-(([[:digit:]]+)?(\.([[:digit:]]+))*([a-z])?(_(alpha|beta|pre|rc|p)([[:digit:]]*))*(-(r([[:digit:]]+)))?))?

它适用于没有版本的字符串,但似乎可选组阻止它匹配上面这样的稍微完整的字符串,它得到:

Version specifier: 
Category: category
Package name: package-name-12345.25b_rc10-r7
Version: 
Version, major: 
Version, minor: 
Version, letter: 
Version, patch type: 
Version, patch level: 
Version, revision number:

编辑:老虎机

两个可选部件怎么样?

除上述之外,还必须匹配插槽。插槽将像这样匹配:

:(([[:digit:]]+)(\.([[:digit:]]+))*)?

现在,有一个类别/名称部分:

([<>]?=?)(([^\/]+)\/)?([^[:space:]]+)

后跟任一版本: -(([[:digit:]]+)(\.([[:digit:]]+))*)([a-z])?(_(alpha|beta|pre|rc|p)([[:digit:]]*))*(-(r([[:digit:]]+))?)?

一个槽 :(([[:digit:]]+)(\.([[:digit:]]+))*)?

或两者,版本和插槽,按此顺序。

请注意,版本以-, 作为分隔符开头,插槽以:, 作为分隔符开头。

我能想到的只有:

if [[ "$1" =~ ^${CATEGORY_PACKAGE}-${VERSION}:${SLOT}$ ]] ; then
    # use "${BASH_REMATCH[n]}" here to capture groups contents
else
    if [[ "$1" =~ ^${CATEGORY_PACKAGE}:${SLOT}$ ]] ; then
        # use "${BASH_REMATCH[n]}" here to capture groups contents
    else
        if [[ "$1" =~ ^${CATEGORY_PACKAGE}-${VERSION}$ ]] || [[ "$1" =~ ^${CATEGORY_PACKAGE}$ ]] ; then
        # use "${BASH_REMATCH[n]}" here to capture groups contents
        fi
    fi
fi

这是一个完整的解决方案吗?有更好的版本吗?是否有基于选项的 POSIX 解决方案,例如<category-name>(<slot option>|<version option>|<version:slot> option)

编辑:

我认为 bash 可以处理一个表达式中的可选组,但我无法更改([^[:space:]:]+)以处理hyphen.如何hyphen包含在内,因为似乎既不能作为范围中的第一个,也不能作为最后一个?

答案1

为了理智起见,在这种情况下,IMO 最好只使用多个正则表达式,而不是将所有可能的情况填充到单个正则表达式中:

base_package_name_regex='^([<>]?=?)(([^/]+)/)?([^[:space:]]+)'
version_regex='(([[:digit:]]+)?(\.([[:digit:]]+))*([a-z])?(_(alpha|beta|pre|rc|p)([[:digit:]]*))*(-(r([[:digit:]]+))?)?)?$ '
if [[ "$1" =~ $base_package_name_regex-$version_regex ]] || # package with version number
    [[ "$1" =~ $base_package_name_regex ]]  #  package without version number

then
    # use "${BASH_REMATCH[n]}" here to capture groups contents
fi

另请注意,在 POSIX ERE 中,\/匹配内容未指定,并且[^\/]匹配除反斜杠或斜杠之外的任何字符。我已经删除了那些虚假的反斜杠。

答案2

如果你必须使用 shell,你可以切换到zsh。然后你可以使用 PCRE 而不是 POSIX ERE,

  • 你会得到这里需要的非贪婪运算符[^[:space:]]+(我们可以在 PCRE 中将其缩短\S+,并将其更改为非贪婪\S+?变体),而不是吞噬所有内容直到最后,从而没有机会匹配版本
  • 你可以使正则表达式更清晰(?x)
  • 仅捕获您需要捕获的内容(用于(?: ...)非捕获组)。
set -o rematchpcre
field_names=(
  version_specifier
  category
  package
  version
  major
  minor
  letter
  patch_type
  patch_level
  revision
)

typeset -A fields

if [[ $1 =~ '(?x)
  ^
  (?<version_specifier> [<>]? =? )
  (?: (?<category> [^/]+ ) / )?
  (?<package> \S+? )
  (?:
    -
    (?<version>
      (?<major> \d* )
      (?: \. (?<minor> \d+ ) )*
      (?<letter> [a-z] )?
      (?:
    _(?<patch_type> alpha|beta|pre|rc|p )
    (?<patch_level> \d* )
      )*
      (?: - (?: r (?<revision> \d+ ) )? )?
    )?
  )?
  \z' ]]; then
  fields=( "${(@)field_names:^match}" )
  typeset -p fields
fi

请注意,我们在这里命名捕获组,(?<name> ...)但这仅用于文档目的,捕获组存储在简单数组中$match,zsh 不支持通过像%+{name}of 之类的名称检索它们perl

该脚本给出了:

$ ./that-script '<category/package-name-12345.25b_rc10-r7'
typeset -A fields=(
 [category]=category
 [letter]=b
 [major]=12345
 [minor]=25
 [package]=package-name
 [patch_level]=10
 [patch_type]=rc
 [revision]=7
 [version]=12345.25b_rc10-r7
 [version_specifier]='<'
)
$ ./that-script '<category/package-name'
typeset -A fields=(
 [category]=category
 [package]=package-name
 [version_specifier]='<'
)
$ ./that-script package-name
typeset -A fields=(
 [category]=''
 [package]=package-name
 [version_specifier]=''
)

相关内容