정규식 ".+"가 예상대로 작동하지 않는 이유는 무엇입니까? [복사]

정규식 ".+"가 예상대로 작동하지 않는 이유는 무엇입니까? [복사]
[root@localhost opt]# cat cfg
key = value
[root@localhost opt]# grep 'key\s*=\s*.+' cfg
[root@localhost opt]# 

내 의도는 다음과 같습니다. =기호 뒤에는 0개 이상의 공백이 올 수 있지만 뒤에는 공백이 아닌 문자가 하나 이상 와야 합니다.

이 줄은 왜 출력되지 않습니까 key = value?

답변1

관찰하다:

$ grep 'key\s*=\s*.+' cfg
$ grep 'key\s*=\s*.\+' cfg
key = value
$ grep -E 'key\s*=\s*.+' cfg
key = value

기본 정규식(BRE, 기본값)에서는 +더하기 기호를 나타냅니다. GNU 확장으로서 하나 이상의 이전 문자를 나타내는 데 사용할 수 있습니다 \+. ?, {, |에도 마찬가지입니다 (. 백슬래시로 이스케이프하지 않는 한 BRE에서는 일반 문자로 처리됩니다.

확장 정규식을 사용하면 규칙이 변경됩니다 -E. ERE의 경우 백슬래시는 필요하지 않으며 일반적으로 +하나 이상의 이전 문자를 나타냅니다. ERE에서는 \+일반 일반 더하기 기호를 나타냅니다.

답변2

key\s*=\s*.+

GNU ERE 구문입니다( \s공백 문자와 일치하고 +하나 이상의 선행 원자와 일치한다고 가정). 따라서 GNU 구현이 필요 grep하고 해당 -E옵션을 전달합니다.

그러나 그것조차 별로 의미가 없다.

첫 번째

grep 'key\s*=\s*.+'

기능적으로 동일

grep 'key\s*=\s*.'

왜냐하면 문자열이 일치하면 anything.+문자열도 일치 anything.하고 그 반대도 마찬가지이기 때문입니다.

또한 공백 문자도 문자입니다. \s*게임 이후로0이상의 공백 문자, key\s*=\s*.기능적으로 key\s*=.(line 포함 key<optional-spaces>=<one-character-space-or-not>)과 동일합니다.

원하는 곳은 다음과 같습니다.

grep 'key\s*=\s*\S'

의 오른쪽에서 공백이 아닌 문자를 하나 이상 찾아야 하며 =해당 기능은 다음과 동일합니다.

grep 'key\s*=.*\S'

또한 key = foo일치하지만 nonkey = foo. key줄의 시작 부분에서만 찾으려면 앵커를 사용하여 다음을 요청해야 합니다 ^.

grep '^key\s*=.*\S'

또는 -x정규식을 사용하여 전체 줄을 일치시킵니다.

grep -x 'key\s*=.*\S.*'

에 해당하는 표준은 ( for ) \s입니다 .[[:space:]][^[:space:]]\S

이 요구 사항을 충족하는 또 다른 방법은 특정 정규식(예: PCRE)에서 스프레드 연산자를 사용하여 역추적을 방지하는 것입니다.

key=\s*.key= 정규식 엔진이 \s*다음 공백 문자를 탐욕스럽게 반복하여 1을 찾은 다음 줄 끝에 도달했기 때문에 =일치할 수 없다는 것을 깨달았기 때문에 일치합니다 ..역추적다음 일치 항목 (여기서는 공백 문자)이 일치할 수 \s있도록 더 적은 일치 항목(이 경우 0)을 사용해 보십시오 ..

-PGNU 옵션과 마찬가지로 PCRE를 사용하면 다음 과 같이 grep작성할 수 있습니다.

 grep -P '^key\s*=(?>\s*).'

(?>...)구문은 역추적을 방지합니다. 따라서 \s*역추적할 수 없는 상태에서 최대한 많은 공백 문자를 먹게 되므로 공백 뒤에 공백이 아닌 문자가 하나 이상 올 경우에만 일치가 발생합니다.

$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=\s*.'
key=
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep -P '^key\s*=(?>\s*).'
key= a
$ printf 'key=%s\n' '' ' ' ' a' | grep '^key\s*=.*\S'
key= a

관련 정보