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:

편집하다:슬롯 머신

옵션 부품 2개는 어떻습니까?

위 사항 외에도 슬롯이 일치해야 합니다. 슬롯은 다음과 같이 일치합니다.

:(([[: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)?

편집하다:

([^[:space:]:]+)bash가 표현식에서 선택적 그룹을 처리할 수 있다고 생각했지만 이를 처리하도록 변경할 수는 없습니다 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

쉘을 사용해야 하는 경우 로 전환할 수 있습니다 zsh. 그런 다음 POSIX ERE 대신 PCRE를 사용할 수 있습니다.

  • 끝까지 모든 것을 먹어치우고 버전이 일치할 가능성을 남기지 않고 여기에서 필요한 탐욕스럽지 않은 연산자를 얻을 수 있습니다 [^[: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> ...)하지만 이것은 문서화 목적으로만 사용되며 캡처 그룹은 간단한 배열에 저장되며 zsh는 of와 ​​같은 이름으로 검색하는 것을 $match지원하지 않습니다 .%+{name}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]=''
)

관련 정보