匹配模式并有选择地在大文件中插入文本

时间:2019-03-22 06:59:00

标签: regex sed replace

我正在处理非常大的代码,需要重命名许多API。这包括所有API的前缀。我的API名称类似于:

MyfoobarFilename_FooFighterDoMeAFavor(MyfoobarFilename_FooFighter *FighterInstance, void *Favor)
MyfoobarFilename_FooFighterGetInLine(MyfoobarFilename_FooFighter *FighterInstance, void *Line)
MyfoobarFilename_FooFighterSetATicketForMe(MyfoobarFilename_FooFighter *FighterInstance, void *Ticket)
MyfoobarFilename_FooFighterHelpMeNowInterrupt(MyfoobarFilename_FooFighter *FighterInstance, void *FighterReference)
MyfoobarFilename_FooFighterAreYouOk(MyfoobarFilename_FooFighter *FighterInstance, unsigned int HowAreYou)

我想通过在API名称上添加下划线来更改所有这些名称。
我希望结果如下所示,

MyfoobarFilename_FooFighter_DoMeAFavor(MyfoobarFilename_FooFighter *FighterInstance, void *Favor)
MyfoobarFilename_FooFighter_GetInLine(MyfoobarFilename_FooFighter *FighterInstance, void *Line)
MyfoobarFilename_FooFighter_SetATicketForMe(MyfoobarFilename_FooFighter *FighterInstance, void *Ticket)
MyfoobarFilename_FooFighter_HelpMeNowInterrupt(MyfoobarFilename_FooFighter *FighterInstance, void *FighterReference)
MyfoobarFilename_FooFighter_AreYouOk(MyfoobarFilename_FooFighter *FighterInstance, unsigned int HowAreYou)

但是,我只想替换API名称中的文本,而不替换代码中的其他位置。我可以使用s/FooFighter/FooFighter_/g,但这也会做出我不想要的更改。

通过使用脚本并执行grep并替换,我可以通过多种方式实现这一目标。
但是,必须有一种使用单个命令(希望使用sed)执行此操作的更快,更聪明的方法。有人可以启发我怎么做。

谢谢。

2 个答案:

答案 0 :(得分:1)

我建议使用以下sed表达式(使用Basic Regular Expression语法):

s/\(\bMyfoobarFilename_FooFighter\)\([a-zA-Z0-9_]\+\b\)/\1_\2/g

其中

  • \b转义序列代表“单词边界”(GNU扩展);
  • \( ... \)创建一个可以在s/regexp/replacement/命令的“替换”部分中引用的组;
  • \1\2是对第一和第二组的引用;
  • g指示 sed 将替换项应用于“ regexp”的所有匹配项,而不仅仅是第一个;
  • \+匹配一个或多个(GNU扩展名)。

Perl替代GNU sed

正如@WiktorStribiżew和@Ed Morton所说,\+\b是GNU扩展。您可以将\+替换为\{1,\}。但是我找不到\b的适当POSIX兼容替代品。如果GNU sed不可用,则可以按以下方式使用Perl:

perl -i -npe \
's/(\bMyfoobarFilename_FooFighter)(\p{L}+\b)/\1_\2/g' file

其中\p{L}Unicode character property代表字母。其余部分与上面的sed表达式相似。


您的代码片段看起来像C / C ++函数原型,因此您可能还想修改函数调用和函数指针,例如:

while (MyfoobarFilename_FooFighterHelpMeNowInterrupt ()
  && MyfoobarFilename_FooFighterSetATicketForMe())
  {
    /* ... */
  }

void (*fptr)(MyfoobarFilename_FooFighter *, void *) =
  &MyfoobarFilename_FooFighterGetInLine;

答案 1 :(得分:0)

尝试一下:

sed "s/\(MyfoobarFilename_FooFighter\)\(\S*(\)/\1_\2/g" file.txt

说明:

s/                                  # substitute
\(MyfoobarFilename_FooFighter\)     # searchstring in \1
\(\S*(\)                            # the rest to the bracket in \2
/\1_\2                              # insert '_' between \1 and \2
/g                                  # global