我有一个替换列表,如下所示:
search_and -> replace
big_boy -> bb
little_boy -> lb
good_dog -> gd
...
我需要替换上述内容,但同时避免匹配更长的字符串,如下所示:
big_boys
good_little_boy
我试过这个:
sed -i -r "s/$(\W){search}(\W)/$\1{replacement}\2/g"
但是,当字符串(在本例中为“good_dog”)出现在行尾时,上述方法不起作用,如下所示:
Mary had a 'little_boy', good_little_boy, $big_boy, big_boys and good_dog
Mary had a 'lb', good_little_boy, $bb, big_boys and good_dog
我怀疑当字符串也出现在行开头时,上面的方法是否有效。有没有好的方法可以进行查找和替换呢?
答案1
如果您正在使用 GNU sed(裸露-i
表明您正在使用),那么有一个“词边界”转义\b
:
sed -i "s/\b$SEARCH\b/$REPLACE/g"
\b
在单词边界上完全匹配:一侧的字符是“单词”字符,而另一侧的字符不是。它是零宽度匹配,因此您不需要使用捕获子组来保留 和 的\1
值\2
。还有\B
一种,恰恰相反。
如果您不使用 GNU sed,则可以在捕获子模式中使用行首和行尾的交替:(\W|^)
。这将匹配非单词字符或行的开头,并且(\W|$)
将匹配非单词字符或行的结尾。在这种情况下,您仍按原样使用\1
and 。无论如何,\2
一些非 GNUsed
确实支持\b
,至少在扩展模式下,所以无论如何都值得一试。
答案2
如果你想要更便携,你可以使用\<
和\>
:
sed -i "s/\<$SEARCH\>/$REPLACE/g" file
\<
并\>
在 gsed、ssed、sed15、sed16、sedmod 中工作。
\b
并且\B
仅在 gsed 中工作。
在 中Mac OSX
,您必须使用以下语法:
sed -i '' -e "/[[:<:]]$SEARCH[[:>:]]/$REPLACE/g" file
答案3
您还可以使用 perl,它应该支持\b
所有平台。假设您的替换列表采用您显示的格式(用 分隔->
),您可以执行以下操作:
perl -F"->" -ane 'chomp;$rep{$F[0]}=${$F[1]};
END{open(A,"file");
while(<A>){
s/\b$_\b/$rep{$_}/g for keys(%rep);
print
}
}' replacements
解释
使
-a
perl 像 awk 一样运行,自动将字段拆分到数组中,第一个字段、第二个字段等都是@F
如此。设置输入字段分隔符,就像awk 中一样。这意味着“逐行读取输入文件并将给定的脚本应用到每一行”。$F[0]
$F[1]
-F
-F
-n
-e
chomp
\n
:从行尾删除换行符 ( )。$rep{$F[0]}=${$F[1]};
:这会填充哈希%rep
,使要替换的模式(第一个字段$F[0]
)成为键,替换 ($F[1]
) 成为值。 * :在读取END{}
输入文件 ( ) 后执行。replacements
open(A,"file")
:使用 filehandle 打开文件file
进行读取A
。while (<A>)
:逐行读取文件。s/// for keys(%rep)
:这将迭代哈希的所有键%rep
,将每个键保存为特殊变量$_
。是s///
替换运算符,并且进行与中所解释的相同的替换迈克尔的回答。
您还可以通读该文件并使用sed
其他答案中所示的内容:
$ sed 's/->/\t/' replacements |
while IFS=$'\t' read from to; do sed -i "s/\b$from\b/$to/g" file; done