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'
答え2
単純なケースに対する簡単な解決策があるので、.?+*{}()[]\/ がなく、よりエキゾチックな sed エントリを持たないきれいで簡単なキーワードがある場合は、sed を使用してリストペアを次に転送できます。 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
正規表現に入れるときに特別な意味を持つスラッシュなどの項目をエスケープするには、代替リストを前処理する必要があります。まずエスケープしてから繰り返しに使用します。
置換を実行するために使用する関数によっては、文字列を文字通り処理するためにいくつかのフラグを追加することもできます。解決策の一部を見て、解決策を実行する正しい方法を提案します。