I have a file A that contains pairs of strings, one per line:
\old1 \new1
\old2 \new2
.....
I would like to iterate over file A, and for each line perform the replacement (e.g. "\old1 -> \new1") globally in some file B. I had no trouble getting it to work without backslashes using sed or perl -pi -e using something like the following:
while read -r line
do
set -- $line
sed -i -e s/$1/$2/g target
done < replacements
However, I can't figure out how to make either sed
or perl
treat the backslashes verbatim in the replacement strings. Is there a clean solution for this?
Best Answer
You'll need to escape all characters that are special in regexps, not just backslashes but also
[.*^$
and thes
delimiter (for sed). In Perl, use thequotemeta
function.A further issue with your attempt is that when you run
set -- $line
, the shell performs its own expansion: it performs globbing in addition to word splitting, so if your line containsa* b*
and there are files calleda1
anda2
in the current directory then you'll be replacinga1
witha2
. You need to turn off globbing withset -f
in this approach.Here's a solution that mangles the replacement list directly into a list of sed arguments. It assumes that there is no space character in the source and replacement texts, but anything other than a space and a newline should be treated correctly. The first replacement adds a
\
before the characters that need protecting, and the second replacement turns each line fromfoo bar
into-e s/foo/bar/g
. Warning, untested.In Perl, you'll have fewer issues with quoting if you just let Perl read the replacement file directly. Again, untested.