搜索并替换不是其他字符串的子字符串的字符串

搜索并替换不是其他字符串的子字符串的字符串

我有一个替换列表,如下所示:

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|$)将匹配非单词字符或行的结尾。在这种情况下,您仍按原样使用\1and 。无论如何,\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

解释

  • 使-aperl 像 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

相关内容