첫 번째 문자열 과 두 번째 문자열 사이의 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
답변1
Sed 정규식은 가장 긴 일치 항목과 일치합니다. Sed는 non-greedy와 동등한 기능이 없습니다.
우리가 해야 할 일은 일치하는 것
AB
,
이어서- 제외한 모든
AC
수량 AC
불행히도 sed
#2는 수행할 수 없습니다. 적어도 다중 문자 정규 표현식에서는 수행할 수 없습니다. 물론, 단일 문자 정규식 @
( )의 경우 이 또는 를 [123]
수행할 수 있습니다 . 따라서 sed 의 모든 항목을 변경한 다음 검색하여 sed의 제한 사항을 해결할 수 있습니다.[^@]*
[^123]*
AC
@
AB
,
이어서@
제외한
모든 것@
이와 같이:
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를 설정하면 안 됩니다.)
-z
NUL로 구분된 입력(예: 출력)을 처리하기 위해 GNU 플래그와 함께 sed를 사용하는 경우 find ... -print0
NUL은 패턴 공간에 나타나지 않으며 여기서 NUL은 대체하기에 좋은 선택입니다.
printf
NUL은 bash 변수에 나타날 수 없지만 명령 에는 포함될 수 있습니다 . 입력 문자열에 NUL을 포함한 모든 문자가 포함될 수 있는 경우 다음을 참조하세요.Stefan Chazeras의 답변이것은 깔끔한 이스케이프 방법을 추가합니다.
답변2
비탐욕적 일치 수행단일 문자, 일치를 종료하는 문자를 제외한 모든 문자와 일치합니다.
그리디 매칭:
$ echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar
탐욕스럽지 않은 매칭:
$ echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
답변3
일부 sed
구현에서는 이를 지원합니다.ssed
PCRE 모드가 있습니다:
ssed -R 's/AB.*?AC/XXX/'
AT&T AST sed*?
탐욕스럽지 않은 버전 으로 연산자를 지원합니다.*
확장하다(와 함께 -E
) 및향상된( -A
정규식 사용).
sed -E 's/AB.*?AC/XXX/'
sed -A 's/AB.*?AC/XXX/'
이 구현 및 보다 일반적으로 해당 -E
/ -A
패턴에서는 Perl과 유사한 정규 표현식을 내부적으로 사용할 수 있습니다 (?P:perl-like regexp here)
. 그러나 위에 표시된 것처럼 연산자에는 이것이 필요하지 않습니다 *?
.
그것은향상된정규식에는 결합 연산자와 부정 연산자도 있습니다.
sed -A 's/AB(.*&(.*AC.*)!)AC/XXX/'
이식 가능한 방법은 다음 기술을 사용할 수 있습니다. 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/; # 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/;s/\n/AC/g'
sed
이는 일반적으로 다른 구현에서는 지원하지 않기 때문에 작동하지 않습니다 [^\n]
. GNU의 경우 sed
POSIX 호환성이 활성화되지 않았는지 확인해야 합니다(예: POSIXLY_CORRECT 환경 변수 사용).
답변4
해결책은 매우 간단합니다. .*
욕심을 가지되 완전히 욕심을 부리지는 마세요. ssABteAstACABnnACss
regexp 와 일치하는 것을 고려하세요 AB.*AC
. AC
다음 내용은 실제로 .*
일치해야 합니다. 문제는 욕심 때문에 .*
후속 AC
경기가마지막 AC
첫 번째보다는. 정규식의 리터럴이 ssABteAstACABnn의 마지막 리터럴과 일치하는 동안 .*
첫 번째 것을 먹습니다 .AC
AC
교류봄 여름 시즌. 이런 일이 발생하지 않도록 하려면 AC
첫 번째 것을 다른 것으로 바꾸십시오.말도 안 되는두 번째 것과 다른 모든 것을 구별하십시오.
echo ssABteAstACABnnACss | sed 's/AC/-foobar-/; s/AB.*-foobar-/XXX/'
ssXXXABnnACss
.*
이것과 정규식 외에는 아무것도 없기 때문에 탐욕은 이제 in의 발밑에서 멈출 것입니다 .-foobar-
ssABteAst-foobar-ABnnACss
-foobar-
-foobar-
-foobar-
~ 해야 하다게임을 하자. 이전 문제는 정규 표현식 에 두 개의 일치 항목이 있었는데 욕심 AC
때문에 마지막 일치 항목이 선택되었다는 것이었습니다. 그러나 의 경우 단 한 번의 일치만이 가능하며, 이 일치는 이것이 절대 욕심이 아니라는 것을 증명합니다. 버스 정류장은 다음 지역에서만 나타납니다..*
AC
-foobar-
.*
.*
하나다음 정규식의 나머지 부분은 여전히 일치합니다 .*
.
AC
AB
오류가 AC
대체되므로 이 해결 방법이 첫 번째 해결 방법보다 먼저 나타나면 실패할 것입니다 -foobar-
. 예를 들어, 첫 번째 대체 sed
후에 ACssABteAstACABnnACss
는 가 됩니다 -foobar-ssABteAstACABnnACss
. 따라서 일치하는 항목을 찾을 수 없습니다 AB.*-foobar-
. 그러나 시퀀스가 항상...AB...AC...AB...AC...인 경우 이 솔루션은 성공합니다.