sed 조건부 분기 "t"는 마지막 대체가 실패한 경우에도 계속 분기됩니다.

sed 조건부 분기 "t"는 마지막 대체가 실패한 경우에도 계속 분기됩니다.

다음과 같은 sed 표현식이 있습니다.

echo 'abcabcabc' | sed -n ':-A s/a/x/1; s/a/&/2;t-A; p'

마지막 항목을 제외한 모든 항목으로 'a'교체 해야 한다고 합니다 'x'. 따라서 예상되는 결과는 다음과 같습니다.

xbcxbcabc

그러나 실제 출력은 다음과 같습니다.

xbcxbcxbc

모두 'a'다음으로 교체'x'

나는 이미 다음과 같은 비슷한 질문이 있다는 것을 알고 있습니다. 각 줄의 마지막 문자를 제외한 모든 문자 바꾸기

하지만 여기서는 sed 조건 분기를 사용하여 다른 접근 방식을 시도하고 있습니다.

내 자신의 이해를 사용하여 내 sed 표현을 분석하겠습니다.

첫 번째는 sed 표현식입니다.

echo 'abcabcabc' | sed -n ':-A s/a/x/1; s/a/&/2;t-A; p'

abcabcabcsed를 사용하여 패턴 공간으로 가져옵니다.

그런 다음 라벨을 설정하세요.:-A

그런 다음 s/a/x/1;첫 번째 항목 'a''x'. 이제 패턴 공간에는 다음이 포함됩니다.xbcabcabc

s/a/&/2;패턴 공간에 둘 다 포함되어 있는지 확인하여 둘 다 자체로 대체 'a'합니다 . 따라서 패턴 공간에는 여전히 다음이 포함됩니다.'a'&xbcabcabc

t-A가장 최근 교체가 성공했으므로 레이블로 다시 이동합니다.-A

태그부터 시작하여 -A이 작업을 다시 수행하고 s/a/x/1;패턴 공간의 내용을 이것에서 xbcabcabc이것 으로 변경합니다.xbcxbcabc

s/a/&/2두 개가 더 있는지 확인합니다 'a'. 이번에는 패턴 공간에 이것이 포함되어 있지만 xbcxbcabc두 개가 없으므로 'a'대체가 실패합니다.

t-A왜냐하면 가장 최근에 교체한 것이실패하다, 레이블로 다시 점프해서는 안 되지만 패턴 공간에 있는 내용을 -A계속 인쇄한 다음 종료해야 합니다. 하지만 대신에 교체가 이루어지더라도pxbcxbcabc실패하다다시 태그로 돌아가서 -A나머지 부분을 'a'대체합니다 'x'. 그래서 결과는 이렇습니다xbcxbcxbc

l표현식 사이에 삽입하는 경우 :

 echo 'abcabcabc' | sed -n ':-A s/a/x/1;  l; s/a/&/2;t-A; p'

산출:

xbcabcabc$
xbcxbcabc$
xbcxbcxbc$
xbcxbcxbc$
xbcxbcxbc

패턴 공간에 이것이 포함되어 있더라도 다시 분기되는 것을 볼 수 있습니다.xbcxbcabc

그렇다면 내가 여기서 무엇을 놓치고 있는 걸까요?

답변1

s/a/&/2두 번째 것을 그 자체로 대체한다는 점에 유의하십시오 a. . a​다시 말하지만, 항상 (첫 번째 것을 다음으로 대체) 과 동일합니다 s/a/x/1. 이것은 질문과 관련이 없지만 여전히 다른 상황에서 당신을 괴롭힐 수 있는 오해입니다.s/a/x/ax

GNU 매뉴얼에 따르면, t마지막 입력 라인을 읽은 이후 성공적인 대체가 발생한 경우 명령이 분기됩니다 t. 단, 그 이후에 다른 명령이 트리거 되지 않는 한은 다음과 같습니다.sed

t label
s///마지막 입력 줄을 읽은 후 a 가 성공적으로 대체된 경우 로 분기하고 , 마지막 또는 command 를 생략하면 스크립트 끝으로 분기합니다.tTlabellabel

동일한 명령에 대한 POSIX 사양이에 동의합니다:

[2addr]t [label]
시험. 마지막 입력 행을 읽거나 :실행 label한 이후 대체가 이루어졌는지 여부를 사용하여 명령 동사 로 분기합니다 t. 지정하지 않으면 label스크립트 끝으로 분기됩니다.

요약하자면, 단일 입력 줄에 대해 명령이 성공하면 s가장 최근 명령 이후 항상 지정된 레이블로 분기됩니다 t.t

귀하의 데이터는 먼저 로 변환된 xbcabcabc다음 으로 변환됩니다 xbcxbcabc. 이 결과를 얻으면 s반복의 초기 명령이 첫 번째 명령을 a로 성공적으로 대체하므로 명령 분기가 x로 지정됩니다 .txbcxbcxbc

이 문제를 해결하는 한 가지 방법은 추가 t명령과 더미 레이블을 삽입하는 것입니다.

echo abcabcabc |
sed -e :A -e 's/a/x/'  -e tB \
    -e :B -e 's/a/&/2' -e tA

tB첫 번째 명령의 "재설정 성공 플래그"를 실행합니다 s.

답변2

간단하게 유지하고 대신 awk를 사용하는 것은 어떨까요? 예를 들어, GNU awk를 사용하면 세 번째 매개변수를 다음과 같이 설정합니다 match().

$ echo 'abcabcabc' |
    awk '{match($0,/(.*)(a.*)/,t); gsub(/a/,"x",t[1]); print t[1] t[2]}'
xbcxbcabc

또는 awk를 사용하십시오.

$ echo 'abcabcabc' |
    awk '{match($0,/.*a/); t=substr($0,1,RLENGTH-1); gsub(/a/,"x",t); print t substr($0,RLENGTH)}'
xbcxbcabc

s, g, p(-n 포함) 이외의 sed 구성 사용을 고려할 때마다 awk 휴대용 솔루션을 사용하면 더 깨끗하고 간단하며 효율적이고 강력하며 더 나은 솔루션이 거의 확실하다는 점에 유의하세요.

답변3

텍스트를 뒤집고 2를 end로 바꾸고 다시 뒤집을 수 있습니다.

$ echo 'abcabcabc' | rev | sed 's/a/x/2g' | rev
xbcxbcabc

sed의 재귀 기능을 사용하여 연습을 수행하지 않는 한 이 간단한 경우에는 레이블과 루프가 필요하지 않습니다.

관련 정보