sed를 사용하여 전역 일치 항목을 중첩하는 방법은 무엇입니까?

sed를 사용하여 전역 일치 항목을 중첩하는 방법은 무엇입니까?

만약 내가한다면:

sed 's/match/replace/g'

나는 그것이 sed대체할 것이라는 것을 안다.바꾸다모든 사건에 대해성냥줄에. 하지만 만약...?

echo "match <please dont match this?>" |
sed 's/match/replace/g'

...또는...

echo "never match unless <the match is somehow delimited?>" |
sed 's/match/replace/g'

test 또는 branch 루프를 사용하여 개별적으로 재귀적으로 일치시킬 수 있다는 것을 알고 있지만 s///g전역 일치 컨텍스트에서 줄의 일부를 건너뛰려면 어떻게 해야 합니까?

답변1

내용은 sed이렇습니다탐욕스러운. 모든 상황에서 가능한 한 많이 잡아먹습니다. 이는 s///g부분 교체 환경에서 이점을 제공할 수 있습니다. 귀하의 \(그룹이\) *0 이상문자열 일치는 sed어떤 경우에도 첫 번째 문자열을 전역적으로 삼킵니다. g따라서 안정적으로 정의할 수 있다면/이것과 일치/ |이것을 건너뛰세요|이렇게 할 수 있는 경우:

sed 's/\([^<>]*<\)*\(match  *\)*\(remove  *\)*/\1/g
     s/.\{,45\}[^ ]*/&\
/g;  s/\(\n\) */\1/g
' <<INPUT
Never remove any match unless <the match \
you want to remove is somehow delimited.> \
And you can remove any match <per your match \
delimiter as many times as your match occurs \
within the match delimiters.>
INPUT

산출

Never remove any match unless <the you want to
is somehow delimited.> And you can remove any
match <per your delimiter as many times as your
occurs within the delimiters.>

쉘이 여기 문서의 백슬래시에서 개행을 이스케이프하기 때문에 입력은 한 줄입니다. sed45자로 분할하세요.(주거나 받거나)테두리를 만들어 인쇄하세요. 그럼에도 불구하고 보시다시피 두 가지 조건 중 하나가 발생할 때마다성냥또는제거하다밖에 하나<...>경계는 그대로 유지되지만 모든 내부 경계는 출력에서 ​​제거됩니다.

sed이는 일치하는 항목에 적용되는 탐욕 함수입니다 .*0 이상이류. 부정하는 데 한두 단계만 추가하면 됨에도 불구하고 대체가 동일한 방식으로 작동하는 것을 불가능하게 만드는 것은 이러한 탐욕입니다.

이것이 어떻게 작동하는지 명확하게 보기 위해 대체를 수행할 수 있습니다. 그런데 직접 적용하면 일반적으로 별로 유용하지 않습니다.

printf '%s %s\n' '<321Nu0-9mber123>' \
                 'String321strinG' \
                 '<321Nu0-9mber123>' \
                 'String321strinG' |
sed 's/\(<[^<>]*>\)*[0-9]*/\1!/g'

산출

<321Nu0-9mber123>! !S!t!r!i!n!g!s!t!r!i!n!G!
<321Nu0-9mber123>! !S!t!r!i!n!g!s!t!r!i!n!G!

따라서 sed전역 패턴에서 선을 일치시킬 때 특징적인 욕심을 유지하면서 가능한 한 많은 패턴을 일치시키려고 합니다. 모드가 탐욕적일 때의 부작용0 이상지정된 발생이 행의 일부와 일치하지 않습니다.여전히 일치- 빈 문자열과 일치합니다.~ 사이줄 부분의 바이트를 일치시킬 수 없습니다.

위에 보시면 아시겠지만<...>문자열은 영향을 받지 않지만, 그 안에 있는 숫자는끈...사라졌을 뿐만 아니라, sed캐릭터별로 뱅이 삽입되었습니다. 이는 sed빈 문자열과의 모든 일치 항목을 반영합니다. 이것이 바로 이 기술이 g전 세계적으로 유용한 이유입니다.정의하다하나를 수행하는 대신 경기를 대체하십시오.

작동 방식은 다음과 같습니다.

printf '%s\t%s\n' '<321Nu0-9mber123>' \
                'String321strinG' \
                '<321Nu0-9mber123>' \
                'String321strinG' |
sed 's/[0-9]/&\n/g;s/\(<[^<>]*>\)*\n*/\1/g;y/\n/0/'

산출

<302010Nu00-90mber102030>       String321strinG
<302010Nu00-90mber102030>       String321strinG

<이는 매우 간단한 경우인 and에 나타나는 모든 숫자에 0을 추가 >하지만 실제로는 \n전역 대체를 수행하기 위해 이런 방식으로 ewline 문자를 사용할 수 있습니다.어느성냥. 기본 원칙은 다음과 같습니다.

  1. 하다sed 's/match/&\n/g'
  2. 그럼 해sed 's/\(match group\)*\n*/\1/g'
  3. 마지막으로 할 일sed 's/match\n/replace/g'

물론, 이 예제는 단순 목록 예제( <항상 맨 위에 있음 )만을 보여줍니다 >. 둥지도 고려해야합니다. 더 어렵습니다. 때로는 더 어렵습니다. 하지만 음...

sed 's/\([{}]\)\([^{}]*[{}]*\1\)*/\n<&>/g
' <<\INPUT
{{{1!}{2!}{3!}}}outside!{{{4!}}{{5!}}}
INPUT

산출

<{{{1!}{2!}{>3!
<}}}>outside!
<{{{4!}}{{>5!
<}}}>

개행 문자로 그룹을 직렬화합니다. 동일한 유형의 구분 기호를 연속으로 두 번 쌓으면서 각 일치 그룹과 일치하는 구분 기호를 번갈아 가며 작동합니다.(적어도 두 번)부작용은 시가와 종가를 비교하는 것입니다. 즉, 단순화를 위해 나머지 부분에서는 모든 독자가 입력을 준비하기 위해 유사한 방법을 사용할 것이며 중첩은 문제가 되지 않는다고 가정합니다.

본질적으로 이 모든 것의 기본 아이디어는 우선순위를 일치시키는 것입니다. 첫 번째 예는 제거된 문자열과 일치시키기 전에 열린 구분 기호 바로 앞에 있는 구분 기호가 없는 문자 그룹과 일치를 시도하는 방식으로 작동합니다. 첫 번째 세트가 일치하면 교체가 완료되면 일치하는 전체 세트만 자체적으로 교체할 수 있으므로 교체가 어려워지는 이유가 됩니다. 제거하는 것이 더 간단합니다. 일치하는 항목을 교체에서 제외하면 괜찮기 때문입니다.

또한 sed다른 패턴보다 특정 유형의 패턴을 더 강조합니다. 이 작업을 수행할 때 다음을 이해하는 것이 중요합니다.정말지정된 패턴은 항상 패턴보다 더 많은 가중치를 갖습니다.*0 이상사례. 따라서 전역 모드에 이러한 항목을 사용할 때는 *해당 항목만 사용하거나 전혀 사용하지 마십시오. 그렇지 않으면 모든 그룹을 전혀 건너뛰게 될 수 있습니다.

방법은 다음과 같습니다 sed.

관련 정보