我有一个文件 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
您需要转义正则表达式中的所有特殊字符,不仅是反斜杠,还有分隔[.*^$
符s
(对于 sed)。在 Perl 中,使用该quotemeta
函数。
您的尝试的另一个问题是,当您运行 时set -- $line
,shell 会执行自己的扩展:除了分词之外,它还执行通配符,因此如果您的行包含并且当前目录中有名为anda* 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-stuff,你可以传输对列表使用 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
您可能需要预处理替换列表,以转义诸如斜杠之类的内容,这些内容在放入正则表达式时将具有特殊含义。首先转义它们,然后使用它们进行迭代。
根据您用来执行替换的函数,有时您可以添加一些标志来按字面意思处理字符串。如果您展示您的部分解决方案,也许我们可以建议完成它的正确方法。