`. *?`를 사용하는 것이`. *`보다 나은 이유 또는 이유는 무엇입니까?


9

나는 출력을 잡는 동안 사용되는 정규 표현식의 종류와 관련된 SuperUser대해이 질문에 대답 했습니다 .

내가 준 대답은 다음과 같습니다.

 tail -f log | grep "some_string.*some_string"

그런 다음 @Bob 이 내 대답에 대한 세 가지 의견으로 다음과 같이 썼습니다.

.*탐욕스럽고 원하는 것보다 더 많이 캡처 할 수 있습니다. .*?일반적으로 더 좋습니다.

그런 다음

?에 대한 수정 자 *이므로 욕심 많은 기본값 대신 게으르게 만듭니다. PCRE를 가정합니다.

나는을 검색 PCRE했지만 내 대답에서 이것의 중요성을 알 수 없었습니까?

그리고 마지막으로

또한 이것이 쉘 glob이 아닌 정규식 (기본적으로 POSIX 정규식을 수행하는 grep)임을 지적해야합니다.

grep 명령에서 정규식이 무엇인지, 기본 사용법 만 알고 있습니다. 그래서, 나는 그 3 가지 의견을 얻을 수 없었으며 다음과 같은 질문을 염두에 두었습니다.

  • 의 사용의 차이 무엇입니까 .*?대는 .*?
  • 어느 것이 더 좋고 어떤 상황에서? 예를 제공하십시오.

또한 의견을 이해하는 것이 도움이 될 것입니다.


업데이트 : 질문에 대한 답변으로 Regex는 Shell Glob과 어떻게 다릅니 까? @Kusalananda 는 그의 의견 에이 링크 를 제공 했습니다 .

참고 : 필요한 경우 문맥을 참조하기 위해 대답하기 전에이 질문에 대한 나의 대답을 읽으십시오 .


이것은 매우 다른 두 가지 질문입니다. 첫 번째 질문은 unix.stackexchange.com/questions/57957/… 에 의해 답변되는 반면 두 번째 질문은 패턴의 적용에 따라 다릅니다 (모든 상황에서 "더 나은"이라고 말할 수는 없습니다).
Kusalananda

이 질문은 vs. 문제에 대해서만 편집 할 수 있습니다 . 이 사이트에서 "정규 표현식과 쉘 글롭의 차이점"질문이 이미 해결되었습니다. .*.*?
Kusalananda

답변:


7

Ashok는 이미.* 와 의 차이점을 지적.*? 했으므로 추가 정보를 제공하겠습니다.

grep (GNU 버전으로 가정) 문자열을 일치시키는 4 가지 방법을 지원합니다.

  • 고정 문자열
  • 기본 정규 표현식 (BRE)
  • 확장 정규식 (ERE)
  • Perl 호환 정규 표현식 (PCRE)

grep 기본적으로 BRE를 사용합니다.

BRE 및 ERE는 POSIX 의 정규식 장에 설명되어 있으며 PCRE는 공식 웹 사이트에 설명되어 있습니다 . 기능과 구문은 구현마다 다를 수 있습니다.

BRE 나 ERE가 게으름을 지원하지 않는다는 것은 가치가 있습니다 .

인접한 여러 중복 기호 ( '+', '*', '?'및 간격)의 동작은 정의되지 않은 결과를 생성합니다.

따라서 해당 기능을 사용하려면 대신 PCRE를 사용해야합니다.

# BRE greedy
$ grep -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# BRE lazy
$ grep -o 'c.*\?s' <<< 'can cats eat plants?'
can cats eat plants

# ERE greedy
$ grep -E -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# ERE lazy
$ grep -E -o 'c.*?s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE greedy
$ grep -P -o 'c.*s' <<< 'can cats eat plants?'
can cats eat plants

# PCRE lazy
$ grep -P -o 'c.*?s' <<< 'can cats eat plants?'
can cats

편집 1

.*vs 에 대해 조금 설명해 주 .*?시겠습니까?

  • .*"가장 긴" 1 패턴 을 일치시키는 데 사용됩니다 .

  • .*?"가장 짧은" 1 패턴 을 일치시키는 데 사용됩니다 .

내 경험상 가장 원하는 행동은 대개 두 번째입니다.

예를 들어, 다음과 같은 문자열이 있고 html 태그 2 만 일치 시키려고 하지만 그 사이의 내용이 아니라고 가정합니다.

<title>My webpage title</title>

이제 .*vs를 비교하십시오 .*?.

# Greedy
$ grep -P -o '<.*>' <<< '<title>My webpage title</title>'
<title>My webpage title</title>

# Lazy
$ grep -P -o '<.*?>' <<< '<title>My webpage title</title>'
<title>
</title>

1. Kusalananda가 지적했듯이 정규식 문맥에서 "가장 길다"와 "가장 짧다"의 의미는 약간 까다 롭습니다 . 자세한 내용은 공식 문서를 참조하십시오.
2. 정규식으로 HTML을 구문 분석하지 않는 것이 좋습니다 . 이것은 교육 목적의 예일 뿐이며 프로덕션 환경에서는 사용하지 마십시오.


.*vs 에 대해 조금 설명해 주 .*?시겠습니까?
C0deDaedalus

@ C0deDaedalus 업데이트되었습니다.
nxnev

9

다음과 같은 문자열을 사용한다고 가정 해보십시오.

can cats eat plants?

욕심쟁이 c.*s를 사용하면으로 시작 c하고 끝나기 때문에 전체 문자열과 일치 s하며 욕심쟁이 연산자는 s가 마지막으로 나타날 때까지 계속 일치합니다.

반면에 lazy c.*?s를 사용하는 것은 첫 번째 발생 s이 발견 될 때까지만 일치합니다 ( 예 : string) can cats.

위의 예에서 다음을 수집 할 수 있습니다.

"Greedy"는 가능한 가장 긴 문자열을 일치시키는 것을 의미합니다. "게으른"은 가능한 가장 짧은 문자열을 일치시키는 것을 의미합니다. 추가 ?와 같은 정량에 *, +, ?, 또는 {n,m}차종이 게으른.


1
"가장 빠른"은 cats이므로 "가장 짧은"을 엄격하게 시행하지는 않습니다.
Kusalananda

2
@Kusalananda true, 그 의미에서 엄격하지는 않지만 여기서 "가장 짧은"은 c와 s의 첫 번째 발생 사이를 의미합니다.
Ashok Arora

1

문자열은 여러 가지 방법으로 간단하게 일치시킬 수 있습니다.

  1. 정적 문자열로서 (var = 'Hello World!'라고 가정) :

    [ "$var" = "Hello World!" ] && echo yes
    echo "$var" | grep -F "Hello"
    grep -F "Hello" <<<"$var"

  2. 글로브로서 :

    echo ./* # pwd에있는 모든 파일을 나열 합니다 .
    case $var in (*Worl*) echo yes;; (*) echo no;; esac
    [[ "$var" == *"Worl"* ]] && echo yes

    기본 및 확장 글로브가 있습니다. 이 case예제는 기본 globs를 사용합니다. bash [[예제는 확장 globs를 사용합니다. 첫 번째 파일 일치는 extglobbash의 설정과 같은 일부 쉘에서 기본이거나 확장 될 수 있습니다 . 이 경우 둘 다 동일합니다. 그렙은 글로브를 사용할 수 없습니다.

    A의 별표 글로브는 A의 별표에 비해 뭔가 다른 의미 정규식 :

    * matches any number (including none) of모든 캐릭터를 .
    * matches any number (including none) of the선행 요소 .

  3. 기본 정규 표현식 (BRE)으로 :

    echo "$var" | sed 's/W.*d//' # print : 안녕하세요!
    grep -o 'W.*d' <<<"$var" # print World!

    (기본) 쉘 또는 awk에는 BRE가 없습니다.

  4. 확장 정규식 (ERE) :

    [[ "$var" =~ (H.*l) ]] # match : Hello Worl
    echo "$var" | sed -E 's/(d|o)//g' # print : Hell Wrl!
    awk '/W.*d/{print $1}' <<<"$var" # print : 안녕하세요
    grep -oE 'H.*l' <<<"$var" # print : Hello Worl

  5. Perl 호환 정규식 :

    grep -oP 'H.*?l # print : Hel

PCRE에서만 a *?특정 구문 의미를 갖습니다.
별표가 게으르다 (배고프다) : 탐욕 대신 게으름 .

$ grep -oP 'e.*l' <<<"$var"
ello Worl

$ grep -oP 'e.*?l' <<<"$var"
el

이것은 빙산의 일각이다,있다 , 욕심 게으른 하고 유순 또는 possesive . 또한 미리보기와 뒤로보기가 있지만 별표에는 적용되지 않습니다 *.

욕심없는 정규 표현식과 동일한 효과를 얻는 대안이 있습니다.

$ grep -o 'e[^o]*o' <<<"$var"
ello

아이디어는 매우 간단합니다. 점을 사용하지 말고 .다음 문자를 무시하여 일치 시키십시오 [^o]. 웹 태그로 :

$ grep -o '<[^>]*>' <<<'<script type="text/javascript">document.write(5 + 6);</script>'
<script type="text/javascript">
</script>

위의 내용은 모든 @Bob 3 의견을 완전히 명시해야합니다. 역설 :

  • . *는 일반적인 정규 표현식이며 글로브가 아닙니다.
  • 정규식 만 PCRE와 호환 될 수 있습니다.
  • PCRE에서 :? * 수량자를 수정하십시오. .*욕심 .*?이 아닙니다.

질문

  • 의 사용법에 어떤 차이가 있습니까? ? vs. ?

    • A .*?는 PCRE 구문에서만 유효합니다.
    • A .*는 더 휴대하기 편리합니다.
    • 점을 부정 문자 범위로 바꾸면 욕심없는 일치와 같은 효과를 얻을 수 있습니다. [^a]*
  • 어느 것이 더 좋고 어떤 상황에서? 예를 제공하십시오.
    보다 나은? 목표에 달려 있습니다. 더 나은 것은 없으며, 각각 다른 목적으로 유용합니다. 위의 몇 가지 예를 제공했습니다. 더 필요하십니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.