다음과 같은 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'
abcabcabc
sed를 사용하여 패턴 공간으로 가져옵니다.
그런 다음 라벨을 설정하세요.:-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
계속 인쇄한 다음 종료해야 합니다. 하지만 대신에 교체가 이루어지더라도p
xbcxbcabc
실패하다다시 태그로 돌아가서 -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/
a
x
GNU 매뉴얼에 따르면, t
마지막 입력 라인을 읽은 이후 성공적인 대체가 발생한 경우 명령이 분기됩니다 t
. 단, 그 이후에 다른 명령이 트리거 되지 않는 한은 다음과 같습니다.sed
t label
s///
마지막 입력 줄을 읽은 후 a 가 성공적으로 대체된 경우 로 분기하고 , 마지막 또는 command 를 생략하면 스크립트 끝으로 분기합니다.t
T
label
label
동일한 명령에 대한 POSIX 사양이에 동의합니다:
[2addr]t [label]
시험. 마지막 입력 행을 읽거나:
실행label
한 이후 대체가 이루어졌는지 여부를 사용하여 명령 동사 로 분기합니다t
. 지정하지 않으면label
스크립트 끝으로 분기됩니다.
요약하자면, 단일 입력 줄에 대해 명령이 성공하면 s
가장 최근 명령 이후 항상 지정된 레이블로 분기됩니다 t
.t
귀하의 데이터는 먼저 로 변환된 xbcabcabc
다음 으로 변환됩니다 xbcxbcabc
. 이 결과를 얻으면 s
반복의 초기 명령이 첫 번째 명령을 a
로 성공적으로 대체하므로 명령 분기가 x
로 지정됩니다 .t
xbcxbcxbc
이 문제를 해결하는 한 가지 방법은 추가 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의 재귀 기능을 사용하여 연습을 수행하지 않는 한 이 간단한 경우에는 레이블과 루프가 필요하지 않습니다.