1行に1つずつ文字列のペアを含むファイル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引数リストに直接変更するソリューションです。ソースと代替テキストにスペース文字がないと仮定しますが、スペースと改行を除くすべての文字を正しく処理する必要があります。最初の置換は\
保護する必要がある文字の前に追加され、2番目の置換は各行をからに変更し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'