我是 sed 的新手,正在尝试学习一些东西。然而,在使用 sed 删除重复的单词时,我遇到了一个无法解决的问题:
echo "abc abc def ghi ijk ijk" | sed 's/\([a-z][a-z]*\) \1/\1/g'
输出
abc def ghijk ijk
每当一个单词以与下一个单词的第一个字母相同的字母结尾时,它都会这样做。我做错了什么?
答案1
问题是,正则表达式可以匹配部分单词。在您展示的示例中,它将i
一个单词末尾的 与i
下一个单词开头的 匹配。解决方案是坚持让正则表达式匹配整个单词:
$ echo "abc abc def ghi ijk ijk" | sed 's/\<\([a-z][a-z]*\)\> \<\1\>/\1/g'
abc def ghi ijk
在 GNU sed 中,\<
在单词的开头匹配,并\>
在单词的结尾匹配。
更复杂的匹配
在问题的例子中,正则表达式匹配单个重复字符。i i
下面是匹配的示例oat oat
:
$ echo "smoat oats" | sed 's/\([a-z][a-z]*\) \1/\1/g'
smoats
再次,通过坚持完整的单词来解决这个问题:
$ echo "smoat oats" | sed 's/\<\([a-z][a-z]*\)\> \<\1\>/\1/g'
smoat oats
简化
由于字母到空格的转换总是标记单词边界,因此上面正则表达式使用的部分\> \<
是不必要的,因为正则表达式要求两边的字符都是字母。因此,我们可以使用:
$ echo "smoat oats" | sed 's/\<\([a-z][a-z]*\) \1\>/\1/g'
smoat oats
文档
有关 sed 及其正则表达式的精妙之处的更多信息,我推荐Grymoire教程. GNU sed 的终极参考是GNU sed 手册。