正则表达式不匹配特定格式的一种方式和sed的多表达式应用

写这个的初衷是为了完成目下我自己遇到的需求,需要把部分linux下工作的c代码迁移到windows环境下,并以dll库的形式提供。函数需要增加如下声明

__declspec(dllexport) void func( void * arg)

/* __declspec(dllexport)  在后续我直接宏定义为了类似  WIN_DECLARE 的格式*/

我便考虑以正则表达式的形式,自动匹配到函数段,并在函数名的前面增加声明,但函数名可能存在很多种的形式,希望匹配到所有情况不太现实,我主要编写正则表达式适配了以下两种函数声明和实现的情况。

/*函数声明1*/
void func( void * arg);

/*函数声明2*/
void func( void * arg1 , void * arg2 ,
               void * arg3);

/*函数实现1*/
void func( void * arg)
{
   ...
}

/*函数实现2*/
void func( void * arg1 , void * arg2 ,
               void * arg3)
{
   ...
}

针对上述函数声明1的情况,可以用如下正则进行一个简单的匹配,我使用sed命令

sed 's/^\w.*([^()]*);$/WIN_DECLARE &/g;'  test.c

从左到右,分四部分简单说明一下这个表达式

  • ^\w 就是以任意字母,数字或下划线为行的开头(此处忽略了开头有空行的函数声明,是对这种情况的牺牲),目的是为了与 if () 语句的情况区分开
  • .*( 匹配从第二个字符开始到第一个左括号
  • [^()]*);$ 匹配到结尾的 ); 格式,并且从左括号,到结尾之间,包含任意个不存在 左右括号的字符(略过那种长表达式,但是同时也略过了那些带强制类型转换的函数声明,但一般在函数声明中加强制类型转换好像也不太合理)
  • WIN_DECLARE & 是sed ‘s///g’ 表达式的第二部分即替换部分,用 & 表征前面匹配到的完整字符串,然后再加上WIN_DECLARE前缀即可,这也是我自己定义的。

针对其他几种情况,也列举正则表达式如下,但是与如上这种均是大同小异。

/*for 声明2*/
sed 's/^\w.*([^()]*,$/WIN_DECLARE &/g;'  test.c

/*for 实现1*/
sed 's/^\w.*([^()]*)$/WIN_DECLARE &/g;'  test.c

/*for 实现2*/
sed 's/^\w.*([^()]*,$/WIN_DECLARE &/g;'  test.c

用如上的表达式可以自动过滤出绝大部分的函数,并加上对应的声明。
不过这里我可能要考虑几种更细致的处理。

  1. 多次对同一文件使用该表达式,结果仍然是正确的。即使执行两次也不能出现函数头部变成如下的情况。
WIN_DECLARE WIN_DECLARE void func( void * arg)

这是为了保持该表达式的自身的强壮性,因为我可能最终会对一个文件夹中的所有文件都使用该命令,而使用过后,我可能又会往文件夹添加新的文件。故而需要保证已经调整过的文件仍能正常工作。

针对该需求,可以在每次执行表达式前,先把WIN_DECLARE清除掉,使用 sed 的多表达式功能来完成。sed命令可以使用 -e 来在一次匹配中,以从左到右的顺序先后使用表达式来做过滤。具体如下

sed -e 's/^WIN_DECLARE[ \t]*//g' -e 's/^\w.*([^()]*);$/WIN_DECLARE &/g' test.c

上述表达式比较简单,就是以 WIN_DECLARE 开头,后面若干个 空格或者tab 均匹配,然后替换掉,完成一步预处理,然后再执行后面的正则匹配。

  1. 我希望部分格式可以做特殊处理(过滤),即虽然也是函数,但不添加上述前缀,比如函数头部为static时,我不希望增加上述前缀,表达式也需要满足这种特殊情况。

我了解一下资料,发现sed的一种特殊表达式可以满足需求,即取反的组合表达式,列举如下:

sed -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g' test.c

因为一般static的声明只在c的函数实现中有,故而我这里的例子是针对上面的函数实现1的。后半段与前面是一致的,主要在于如下的前半段

/.*static.*([^()]*)$/! 

这也是属于一个表达匹配式中的一部分,然后是匹配到这一行的开头部分带有 static字段,static 前后均可以有任意数量的字符,同时后段匹配 我们对函数实现的 正则表达式匹配,即匹配 ([^()]*)$ ,我们就认为这是static声明的函数,而这种函数显然是内部使用,不需要通过 dllexport的声明暴露出来,故而在表达式的末尾使用 ‘!’ 来取非,意味着我们后面匹配的表达式的前提,是不匹配这前半段的表达式。

将上述的几个流程综合在一起,单独针对函数实现1 这种情况,我们可以得到如下的表达式。

sed -e 's/^WIN_DECLARE[ \t]*//g' -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g' test.c

当然,我们也可以把函数实现1和 函数实现2这两种情况,用sed的 -e 多表达式来实现。在处理上效率会更高。

sed -e 's/^WIN_DECLARE[ \t]*//g' -e '/.*static.*([^()]*)$/! s/^\w.*([^()]*)$/WIN_DECLARE &/g'  -e '/.*static.*([^()]*,$/! s/^\w.*([^()]*,$/WIN_DECLARE &/g'  test.c

这几乎是我写过的最长的正则表达式组合语句。完成之后我也想到,这种思路可能在其他地方也可以应用上。关键点就是我在标题中描述两个点,一是多表达式应用,二是通过sed实现过率特定格式的办法。故而我撰写了这篇博客,也算完成一个任务,同时也是我第一次对正则表达式做这种相对复杂的应用,故而特此记录一下。

备注:
实际对文件做修改,当然要需要使用 sed -i 选项,但是使用这个选项务必慎重,在此,为避免出现错误,在举例的表达式中均不使用 -i选项。

你可能感兴趣的:(linux系统运维)