grep发生意外行为以替换子字符串

问题描述

我在理解如何使用grep来完成一个看似简单的任务时遇到了一些麻烦。我想匹配出现在许多文件中的子字符串,但我想忽略该子字符串以字母或数字开头的情况

例如,我有一堆文件,行如下:

{ some word: ['bar-something','bar-somthing-else'] },{ some text: ['bar-fab','bar-fab-foo','bar-eggs'] },<bar-sometext>Hello World!</bar-sometext>
'bar-foobar-foo'
'bar-foo'

我想替换所有的ket-外观,但是仅在bar前面没有字母或数字的情况下

'bar-foobar-foo'

应更改为

'ket-foobar-foo'

但是我遇到了一些麻烦,因为grep命令与他们自己的规则不一致

让我解释一下:

命令:

git grep -l 'bar-' | xargs sed -i '' -e 's/bar-/ket-/g' 

几乎可以工作,唯一的问题是它也改变了以字母或数字开头的条形:

'bar-foobar-foo''ket-fooket-foo'

要进行一些测试,在进行替换之前,我仅与grep匹配。我期待该命令

grep -E '[^a-zA-Z0-9]ket-' a.file 

很有用,但它也匹配单词ket-之前的任何特殊字符。例如,匹配

<bar-

'bar-

\bar-

(为简单起见,我删除了其余文本,上面突出显示为匹配的文本),而不是仅匹配bar-。为什么这样做?当我不排除字母或数字时,grep与前面的特殊字符不匹配。

我怎么能只替换bar-而不匹配其他任何内容,但是同时忽略了该子字符串前面带有任何字母或数字的任何情况。我给出的示例的预期输出是:

{ some word: ['ket-something','ket-somthing-else'] },{ some text: ['ket-fab','ket-fab-foo','ket-eggs'] },<ket-sometext>Hello World!</ket-sometext>
'ket-foobar-foo'
'ket-foo'

顺便说一句,我使用的是Mac,遇到替换命令时遇到的麻烦

git grep -l 'bar-' | xargs sed -i '' -e 's/bar-/ket-/g'

在我的带有oh-my-zsh终端的Mac上运行良好,我将不胜感激任何看起来像上面命令的答案

预先感谢

解决方法

也许,您应该使用另一种支持后向断言的工具。

perl -pi.bak -e 's/(?<![\p{L}\d])bar/test/g' file.txt
  • -p处理,然后逐行打印<>
  • -i激活就地编辑。 file.txt将以.bak扩展名进行备份,
  • -e表示第一个参数是Perl单行代码,而不是Perl可执行文件,
  • (?<!是断言背后的断言,
  • \p{L}是任何字母。

https://stackoverflow.com/a/6995010/6632736的启发。

,

使用GNU sed:

sed 's/\([^[:alnum:]]\)bar/\1ket/g' file

这是's/pattern/replace/g'形式的sed替换,其中g表示全局。

匹配模式表示:一个非字母数字字符,后跟“ bar”。替换字符是匹配的字符(\1,后跟ket。无论嵌套在匹配模式中括号之间的内容如何,​​都可以重复使用,例如\1 \2等,直到\9

您可以就地执行此操作,就像在示例命令中一样(以及任何特定于macOS的调整)。另外,grep并不用于替换,它仅提取文本,通常没有理由将其与awk或sed一起使用。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...