试图从表单类别/名称版本中的字符串中匹配一些 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]=''
)