SED 정규식과 일치하지 않는 일치 (Perl의. *?) (이 문자열은

내가 사용하고자하는 sed첫 번째 사이의 문자열에 아무것도를 교체 AB하고 처음 의 발생 AC과 (포함) XXX.

예를 들어 ,이 문자열이 있습니다 (이 문자열은 테스트 전용입니다).

ssABteAstACABnnACss

다음과 비슷한 출력을 원합니다 ssXXXABnnACss.


나는 이것을했다 perl:

$ echo 'ssABteAstACABnnACss' | perl -pe 's/AB.*?AC/XXX/'
ssXXXABnnACss

하지만로 구현하고 싶습니다 sed. 다음 (Perl 호환 정규식 사용)이 작동하지 않습니다.

$ echo 'ssABteAstACABnnACss' | sed -re 's/AB.*?AC/XXX/'
ssXXXss



답변

Sed 정규식은 가장 긴 일치 항목과 일치합니다. Sed는 욕심없는 것과 동등하지 않습니다.

분명히 우리가하고 싶은 것은 일치입니다

  1. AB,
    뒤에
  2. 이외의 금액 AC,
    다음
  3. AC

불행히도, sed적어도 다중 문자 정규 표현식에서는 # 2를 할 수 없습니다. 물론, 단일 문자 정규 표현식 등을 위해 @(또는 [123]), 우리가 할 수있는 [^@]*[^123]*. 따라서 모든 발생을 변경 한 다음 검색 AC하여 sed의 한계를 해결할 수 있습니다.@

  1. AB,
    뒤에
  2. 이외의 임의의 수의 @,
    다음에
  3. @

이처럼 :

sed 's/AC/@/g; s/AB[^@]*@/XXX/; s/@/AC/g'

마지막 부분은 일치하지 않는 인스턴스를 @로 변경합니다 AC.

그러나 물론 이것은 입력이 이미 @문자를 포함 할 수 있기 때문에 무모한 접근 방식입니다 . 따라서 일치시킴으로써 거짓 긍정을 얻을 수 있습니다. 그러나 쉘 변수에 NUL ( \x00) 문자가 포함되어 있지 않으므로 NUL은 위의 해결 방법에서 사용하는 것이 좋습니다 @.

$ echo 'ssABteAstACABnnACss' | sed 's/AC/\x00/g; s/AB[^\x00]*\x00/XXX/; s/\x00/AC/g'
ssXXXABnnACss

NUL을 사용하려면 GNU sed가 필요합니다. (GNU 기능을 사용하려면 사용자가 쉘 변수 POSIXLY_CORRECT를 설정하지 않아야합니다.)

GNU의 -z플래그 와 함께 sed를 사용하여 의 출력과 같이 NUL로 분리 된 입력을 처리하는 경우 NUL find ... -print0은 패턴 공간에 없으며 여기서 NUL은 대체를위한 좋은 선택입니다.

NUL은 bash 변수에 속할 수 없지만 printf명령 에 포함시킬 수 있습니다 . 입력 문자열에 NUL을 포함한 모든 문자가 포함될 수 있다면 Stéphane Chazelas의 답변 을 참조하십시오 .


답변

일부 sed구현은이를 지원합니다. ssedPCRE 모드가 있습니다 :

ssed -R 's/AB.*?AC/XXX/g'

AT & T ast sed정규 표현식을 사용할 때 관련과 부정을 가지고 있습니다 .

sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/g'

아마도이 기술을 사용할 수 있습니다. 끝 문자열 (here AC)을 시작 또는 끝 문자열 (예 :와 같이 :) 에서 발생하지 않는 단일 문자로 바꾸십시오. 그렇게하면 s/AB[^:]*://입력에 문자가 나타날 수 있습니다 시작 및 종료 문자열과 충돌하지 않는 이스케이프 메커니즘을 사용하십시오.

예를 들면 :

sed 's/_/_u/g; # use _ as the escape character, escape it
     s/:/_c/g; # escape our replacement character
     s/AC/:/g; # replace the end string
     s/AB[^:]*:/XXX/g; # actual replacement
     s/:/AC/g; # restore the remaining end strings
     s/_c/:/g; # revert escaping
     s/_u/_/g'

GNU의 sed경우, 접근 방식은 개행 문자를 대체 문자로 사용하는 것입니다. sed한 번에 한 줄씩 처리 하기 때문에 패턴 공간에서 줄 바꿈이 발생하지 않으므로 다음을 수행 할 수 있습니다.

sed 's/AC/\n/g;s/AB[^\n]*\n/XXX/g;s/\n/AC/g'

sed지원하지 않기 때문에 다른 구현 에서는 일반적으로 작동 하지 않습니다 [^\n]. GNU sed를 사용하면 POSIXLY_CORRECT 환경 변수와 같이 POSIX 호환성이 활성화되어 있지 않은지 확인해야합니다.


답변

아니요, sed 정규 표현식은 욕심이 일치하지 않습니다.

Perl과 동일하게 “ AC포함하지 않는 항목 ”을 사용하여 모든 텍스트를 첫 번째 항목까지 일치시킬 수 있습니다 . “포함하지 않는 것은” 정규 표현식으로 쉽게 표현할 수 없습니다. 정규 표현식의 부정을 인식하는 정규 표현식이 항상 있지만 부정 정규 표현식은 빠르게 복잡해집니다. 휴대용 sed에서는 부정 정규 표현식이 확장 정규 표현식 (예 : awk)으로 표시되지만 이식 가능한 기본 정규 표현식에는없는 대체를 그룹화해야하기 때문에 전혀 불가능합니다. GNU sed와 같은 일부 sed 버전에는 가능한 모든 정규 표현식을 표현할 수있는 BRE 확장 기능이 있습니다.ACAC.*?ACAC

sed 's/AB\([^A]*\|A[^C]\)*A*AC/XXX/'

정규식을 부정하기가 어렵 기 때문에 일반화되지 않습니다. 대신 할 수있는 것은 일시적으로 선을 변환하는 것입니다. 일부 sed 구현에서는 줄 바꿈을 입력 줄에 표시 할 수 없으므로 줄 바꿈을 마커로 사용할 수 있습니다 (여러 마커가 필요한 경우 줄 바꿈과 다양한 문자 사용).

sed -e 's/AC/\
&/g' -e 's/AB[^\
]*\nAC/XXX/' -e 's/\n//g'

그러나 일부 sed 버전의 문자 집합에서는 백 슬래시 줄 바꿈이 작동하지 않습니다. 특히 이것은 GNU sed에서 작동하지 않습니다. GNU sed는 내장되지 않은 Linux에서의 sed 구현입니다. GNU sed에서는 \n대신 사용할 수 있습니다 .

sed -e 's/AC/\
&/g' -e 's/AB[^\n]*\nAC/XXX/' -e 's/\n//g'

이 특정 경우 첫 번째 AC줄 바꿈으로 바꾸면 충분합니다 . 위에서 제시 한 접근 방식이 더 일반적입니다.

sed에서보다 강력한 접근 방식은 선을 보류 공간에 저장하고, 선의 첫 번째 “관심있는”부분을 제외한 모든 부분을 제거하고, 보류 공간과 패턴 공간을 교환하거나 패턴 공간을 보류 공간에 추가하고 반복하는 것입니다. 그러나 이렇게 복잡한 작업을 시작하면 실제로 awk로 전환하는 것을 고려해야합니다. Awk에는 욕심없는 일치가 없지만 문자열을 분할하고 부품을 변수에 저장할 수 있습니다.


답변

sed-Christoph Sieghart의 욕심없는 매칭

sed에서 욕심없는 일치를 얻는 트릭은 일치하는 문자를 제외하고 모든 문자를 일치시키는 것입니다. 나도 알다시피, 그러나 그것은 귀중한 시간을 낭비하고 쉘 스크립트는 결국 빠르고 쉬워야합니다. 따라서 다른 사람이 필요할 수있는 경우 :

욕심 매칭

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

욕심없는 매칭

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar


답변

귀하의 경우 다음과 같이 닫는 문자를 무효화 할 수 있습니다.

echo 'ssABteAstACABnnACss' | sed 's/AB[^C]*AC/XXX/'


답변

해결책은 매우 간단합니다. .*탐욕 스럽지만 절대적으로 탐욕 스럽지는 않습니다. ssABteAstACABnnACss정규 표현식과 일치하는 것을 고려하십시오 AB.*AC. AC다음이 .*필수 실제로 경기가 있습니다. 문제는 .*탐욕 스럽기 때문에 후속 AC은 첫 번째가 아닌 마지막 과 일치 AC한다는 것입니다. regexp 의 리터럴 이 ssABteAstACABnn AC ss 의 마지막 리터럴 과 일치하는 동안 .*첫 번째 AC를 먹습니다 . 이를 방지하려면 첫 번째 대체 뭔가 말도 두 번째에서와 다른 어떤에서 그것을 차별화.ACAC

echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss

욕심은 .*지금의 기슭에 중지 -foobar-에서 ssABteAst-foobar-ABnnACss다른 없기 때문에 -foobar-이것보다 -foobar-, 그리고 정규 표현식은 -foobar- 반드시 일치해야합니다. 이전 문제는 정규 표현식 AC에 두 개의 일치 항목이 있었지만 .*욕심 때문에 마지막 일치 항목 AC이 선택되었습니다. 그러나을 사용 -foobar-하면 하나의 일치 만 가능하며이 일치는 .*완전히 욕심이 아님을 증명합니다 . 버스 정류소는 다음 정규 표현식 나머지에 대해 하나의 일치 항목 .*만 남아 있는 경우 발생합니다 ..*

이 경우이 솔루션은 실패 할주의 AC첫 번째 전에 나타나는 AB잘못이 있기 때문 AC으로 대체됩니다 -foobar-. 예를 들어, 첫 번째 후 sed치환 ACssABteAstACABnnACss된다 -foobar-ssABteAstACABnnACss; 따라서에 대해 일치하는 항목을 찾을 수 없습니다 AB.*-foobar-. 그러나 시퀀스가 ​​항상 … AB … AC … AB … AC …이면이 솔루션이 성공합니다.


답변

하나 개의 대안은, 그래서 문자열을 변경하는 것입니다 하려는 욕심 경기

echo "ssABtCeCAstACABnnACss" | rev | sed -E "s/(.*)CA.*BA(.*)/\1CA+-+-+-+-BA\2/" | rev

사용 rev하여 일치 기준, 사용 역, 문자열을 반대로 sed…. 결과를 반대로 다음 일반적인 방식에서

ssAB-+-+-+-+ACABnnACss