문자열 "Abbey Street E.2 Buckfast Street"에서 하위 문자열 "Abbey Street E.2"를 대체하여 "Buckfast Street"라는 결과를 얻으려고 합니다.
나는 그것을 시도했지만 :s/[^ ]* [^ ]* //
내가 얻은 것은 "거리"뿐이었습니다.
기본적으로 나는 마지막 공백 문자에서 두 번째 이전의 모든 것을 제거하기 위해 바꾸기 명령을 사용하려고합니다.
내 운영 체제는 MacOs이고 vim을 사용합니다.
또한 위의 바꾸기 명령에 대한 나의 이해는 공백이 아닌 문자를 공백 문자와 일치시킨 다음 공백이 아닌 문자를 공백 문자와 일치시키는 것입니다. 맞습니까?
답변1
sed(테스트하기가 더 쉬운 곳)에서는 필요한 정규식을 작성할 수 있습니다.
A는 [^ ]*
모든 단어와 일치해야 합니다(구두점이 없는 경우). 그래서:
$ a="Abbey Street E.2 Buckfast Street"
$ echo "$a" | sed 's/[^ ]*//'
Street E.2 Buckfast Street
첫 번째 단어가 삭제됩니다. 출력에는 공백이 남아 있습니다. 그런 다음 공간도 제거해야 합니다. 그리고 동일한 작업을 3번 반복하여 앞의 3개 단어를 제거하고 마지막 두 단어는 유지합니다.
$ echo "$a" | sed 's/\([^ ]* \)\{3\}//'
Buckfast Street
하지만 설명에서 당신은 이렇게 말했습니다.두 번째 공백 문자까지, 그건 달라. 6단어 문장에서 3단어를 제거하면 대신 3단어가 남습니다.마지막 두.
따라서 우리는 거꾸로 작업해야 하며 정규식의 효과를 확인하기 위해 각 부분을 캡처하여 별도로 인쇄하겠습니다 |==|
.
단어를 캡처하는 기본 아이디어는 을 사용하는 것입니다 [^ ]*
. 예, 작동합니다(때때로). \
'를 피하려면 -E를 사용하세요 .
$ echo "$a" | sed -E 's/([^ ]*)(.*)/\1|==|\2/'
Abbey|==| Street E.2 Buckfast Street
.*
첫 번째 괄호에 첫 번째 단어를 캡처하고 두 번째 괄호( )에 "나머지 모든 단어"를 캡처합니다. 그러나 정규식을 반대로 바꾸려면 다음을 수행하십시오.
$ echo "$a" | sed -E 's/(.*)([^ ]*)/\1|==|\2/'
Abbey Street E.2 Buckfast Street|==|
여기서 일어나는 일은 .*
모든 것이 캡처되고 다음 부분이 캡처된다는 것입니다.영문자(유효한 결과입니다 *
). 우리에겐 좀 필요해앵커 또는 분리기, 정규 표현식이 특정 문자나 특정 지점의 지점과 일치하도록 강제합니다. 선택한 단어가 실제로 있는지 확인하기 위해 공백을 구분 기호로 사용하고 $를 앵커로 사용할 수 있습니다.마지막끈:
$ echo "$a" | sed -E 's/(.* )([^ ]*)$/\1|==|\2/'
Abbey Street E.2 Buckfast |==|Street
일치했던 공백을 반복하세요.마지막 두 단어:
$ echo "$a" | sed -E 's/(.* )([^ ]* [^ ]*)$/\1|==|\2/'
Abbey Street E.2 |==|Buckfast Street
이제 유지 및/또는 삭제하려는 부분을 선택하십시오.
$ echo "$a" | sed -E 's/(.* )([^ ]* [^ ]*)$/\2/'
Buckfast Street
물론, 이 시점에서 첫 번째 부분을 캡처할 필요는 없습니다.
$ echo "$a" | sed -E 's/.* ([^ ]* [^ ]*)$/\1/'
Buckfast Street
이 ERE에 해당하는 BRE는 vim에서 작동합니다.
:s/.* \([^ ]* [^ ]*\)$/\1/
답변2
vim에서는 다음 ()
과 같은 것을 이스케이프해야 할 수도 있습니다.
:s/.* \(.\+ .\+$\)/\1/
공백이 2개 이상 없으면 줄이 끊어집니다.
답변3
awk를 사용하여 문자열의 마지막 두 단어를 인쇄할 수도 있습니다.
awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
예:
$ echo ""|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
$ echo "1"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
1
$ echo "1 22"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
1 22
$ echo "1 22 333"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
22 333
$ echo "1 22 333 4444"|awk '{printf(NF>1)?$(NF-1)" "$NF"\n":(NF>0)?$NF"\n":""}'
333 4444
sed라면 다음을 사용합니다.
sed 's/^.*\s\([^ \t]\+\)\s\+\([^ \t]\+\)\s*$/\1 \2/g'
예:
$ echo " 1 22 3333 4444 "|sed 's/^.*\s\([^ \t]\+\)\s\+\([^ \t]\+\)\s*$/\1 \2/g'
3333 4444
적은 수의 공백(또는 탭 기호)이 포함된 줄은 올바르게 처리되어야 하고 줄 끝에 추가 공백이 있을 수도 있으므로 이 경우 출력은 두 줄이 공백 단어로 구분되어 복잡도가 증가합니다. . 그러나 이 경우에는 한 단어만 포함된 줄이나 공백만 포함된 줄은 포함되지 않고 그대로 인쇄됩니다. 신경써도 되지만 sed 명령이 더 복잡해지므로 여기에서는 건너뛰겠습니다.
고쳐 쓰다.
MacOS sed인 경우, 다음과 같습니다(더 쉽게 하기 위해 탭을 제외했습니다).
sed 's/^.* \([^ ][^ ]*\) *\([^ ][^ ]*\) *$/\1 \2/g'
예:
$ echo " 1 22 3333 4444 "|sed 's/^.* \([^ ][^ ]*\) *\([^ ][^ ]*\) *$/\1 \2/g'
3333 4444
답변4
당신이 찾고 있는 것은 다음과 같습니다:
:s/^\S*\s\S*\s\S*\s//
^는 줄의 시작을 나타내고,
\s는 "공백"(공백 또는 탭)을 나타내고
, \S는 "공백 없음"을 나타냅니다.
이는 다음과 같이 "축약"될 수 있습니다.
:s/^\(\S*\s\)\{3\}//
"공백이 아닌 모든 수"가 3회 발생하고 그 뒤에 공백이 있음을 나타냅니다.
이는 "Abbey Street E.2"와 일치하여 제거되고 "Buckfast Street"는 유지되어야 합니다.