한 줄에 하나씩 문자열 쌍을 포함하는 파일 A가 있습니다.
\old1 \new1
\old2 \new2
.....
파일 A를 반복하고 파일 B의 각 줄에 대해 전역 교체(예: "\old1 -> \new1")를 수행하고 싶습니다. 백슬래시 없이 쉽게 교체를 완료하려면 sed 또는 perl -pi -e를 사용하세요. 구체적인 작업은 다음과 같습니다.
while read -r line
do
set -- $line
sed -i -e s/$1/$2/g target
done < replacements
그러나 대체 문자열에서 백슬래시를 문자 그대로 만들 sed
거나 처리하는 방법을 모르겠습니다 perl
. 깨끗한 해결책이 있습니까?
답변1
[.*^$
백슬래시뿐만 아니라 구분 기호 ( sed의 경우) 를 포함하여 정규식의 모든 특수 문자를 이스케이프해야 합니다 s
. Perl에서는 이 quotemeta
기능을 사용하세요.
시도의 또 다른 문제는 set -- $line
쉘이 실행될 때 자체 확장을 수행한다는 것입니다. 단어 분리 외에도 와일드카드도 수행하므로 행에 a* b*
이름이 있고 현재 디렉토리에 파일이 있는 경우 대체합니다. 와 함께 . 이 접근 방식에서는 와일드카드를 꺼야 합니다.a1
a2
a1
a2
set -f
다음은 대체 목록을 sed 인수 목록으로 직접 수정하는 솔루션입니다. 소스 및 대체 텍스트에 공백 문자가 없다고 가정하지만 공백과 줄 바꿈을 제외한 모든 문자를 올바르게 처리해야 합니다. 첫 번째 대체는 \
보호해야 할 문자 앞에 추가되고 두 번째 대체는 각 줄을 에서 로 변경 foo bar
합니다 -e s/foo/bar/g
. 경고, 테스트되지 않았습니다.
set -f
sed_args=$(<replacement sed -e 's~[/.*[\\^$]~\\&~g' \
-e 's~^\([^ ]*\) *\([^ ]*\).*~-e s/\1/\2/g~')
sed -i $sed_args target
Perl에서는 Perl이 대체 파일을 직접 읽도록 하면 참조 관련 문제가 줄어듭니다. 다시 말하지만, 테스트되지 않았습니다.
perl -i -pe 'BEGIN {
open R, "<replacement" or die;
while (<R>) {
chomp;
($from, $to, @ignored) = split / +/;
$s{$from} = $to;
}
close R;
$regexp = join("|", map {quotemeta} keys %s);
}
s/($regexp)/$s{$1}/ego'
답변2
간단한 경우에 대한 간단한 솔루션이 있으므로 .?+*{}()[]\/가 없고 더 이국적인 sed 항목이 없는 깨끗하고 간단한 핵심 단어가 있는 경우 sed를 사용하여 Listing 쌍을 다음으로 전송할 수 있습니다. sed 명령 파일:
sed -re 's,(^\\| \\|$),/,g;s/^/s/;s/$/g/' pairs.txt > pairs.sed
sed -f pairs.sed input > output
답변3
이는 패턴 대체와 함께 매개변수 확장을 사용하여 백슬래시를 이스케이프 처리하려는 시도입니다.
$ set -- \\foo \\bar
$ echo $1
\foo
$ echo ${1/\\/\\\\}
\\foo
$ echo "This is \foo to me"
This is \foo to me
$ echo "This is \foo to me" | sed s/${1/\\/\\\\}/${2/\\/\\\\}/
This is \bar to me
$
답변4
정규식에 넣을 때 특별한 의미를 갖는 슬래시와 같은 항목을 이스케이프 처리하려면 대체 목록을 전처리해야 할 수도 있습니다. 먼저 이스케이프 처리한 다음 반복에 사용하세요.
교체를 수행하는 데 사용하는 함수에 따라 문자열을 문자 그대로 처리하기 위해 일부 플래그를 추가할 수도 있습니다. 솔루션의 일부를 보여주시면 해당 솔루션을 수행하는 올바른 방법을 제안해 드릴 수 있습니다.