다음과 같은 대체 목록이 있습니다.
search_and -> replace
big_boy -> bb
little_boy -> lb
good_dog -> gd
...
위의 내용을 바꿔야 하지만 동시에 다음과 같이 긴 문자열을 일치시키는 것을 피해야 합니다.
big_boys
good_little_boy
나는 이것을 시도했습니다 :
sed -i -r "s/$(\W){search}(\W)/$\1{replacement}\2/g"
그러나 다음과 같이 문자열(이 경우 "good_dog")이 줄 끝에 나타나면 위 방법이 작동하지 않습니다.
Mary had a 'little_boy', good_little_boy, $big_boy, big_boys and good_dog
Mary had a 'lb', good_little_boy, $bb, big_boys and good_dog
문자열이 줄의 시작 부분에도 나타날 때 위의 내용이 작동할지 의심됩니다. 찾아 교체할 수 있는 좋은 방법이 있나요?
답변1
GNU sed를 사용하는 경우(과도한 노출은 -i
사용 중임을 나타냄)"단어 경계" 이스케이프\b
:
sed -i "s/\b$SEARCH\b/$REPLACE/g"
\b
단어 경계의 정확한 일치: 한쪽의 문자는 "단어" 문자이지만 다른 쪽의 문자는 그렇지 않습니다. 너비가 0인 일치이므로 sum \1
값을 보존하기 위해 하위 그룹 캡처를 사용할 필요가 없습니다 \2
. \B
정반대인 또 다른 것이 있습니다 .
GNU sed를 사용하지 않는 경우 캡처 하위 모드에서 줄의 시작과 끝을 번갈아 사용할 수 있습니다 (\W|^)
. 이는 단어가 아닌 문자 또는 줄의 시작과 일치하고, (\W|$)
단어가 아닌 문자 또는 줄의 끝과 일치합니다. 이 경우에도 \1
and를 그대로 사용합니다. 어쨌든, \2
GNU가 아닌 일부 제품은 적어도 확장 모드에서 sed
이를 지원하므로 어쨌든 시도해 볼 가치가 있습니다.\b
답변2
이식성을 높이고 싶다면 다음을 사용할 수 \<
있습니다 \>
.
sed -i "s/\<$SEARCH\>/$REPLACE/g" file
\<
\>
gsed, ssed, sed15, sed16, sedmod에서 작동합니다 .
\b
그리고 \B
gsed에서만 작동합니다.
에서는 Mac OSX
다음 구문을 사용해야 합니다.
sed -i '' -e "/[[:<:]]$SEARCH[[:>:]]/$REPLACE/g" file
답변3
\b
모든 플랫폼을 지원하는 Perl을 사용할 수도 있습니다 . 교체 목록이 표시된 형식(구분됨 ->
)이라고 가정하면 다음을 수행할 수 있습니다.
perl -F"->" -ane 'chomp;$rep{$F[0]}=${$F[1]};
END{open(A,"file");
while(<A>){
s/\b$_\b/$rep{$_}/g for keys(%rep);
print
}
}' replacements
설명하다
Perl이 awk처럼 동작하도록 만들어
-a
필드를 배열, 첫 번째 필드, 두 번째 필드@F
등 으로 자동 분할합니다. awk와 마찬가지로 입력 필드 구분자를 설정합니다. 이는 "입력 파일을 한 줄씩 읽고 주어진 스크립트를 각 줄에 적용한다"는 의미입니다.$F[0]
$F[1]
-F
-F
-n
-e
chomp
\n
: 줄 끝에서 개행 문자( )를 제거합니다.$rep{$F[0]}=${$F[1]};
%rep
: 교체할 패턴(첫 번째 필드$F[0]
)이 키이고 교체($F[1]
)가 값이 되도록 해시를 채웁니다 . *:END{}
입력 파일()을 읽은 후 실행됩니다.replacements
open(A,"file")
file
: 파일 핸들을 사용하여 읽기 위해 파일을 엽니다A
.while (<A>)
: 파일을 한 줄씩 읽습니다.s/// for keys(%rep)
: 이것은 해시의 모든 키를 반복하여%rep
각 키를 특수 변수로 저장합니다$_
.s///
대체 연산자이며 설명된 것과 동일한 대체를 수행합니다 .마이클의 대답.
파일을 읽고 sed
다른 답변에 표시된 내용을 사용할 수도 있습니다.
$ sed 's/->/\t/' replacements |
while IFS=$'\t' read from to; do sed -i "s/\b$from\b/$to/g" file; done