linux的sed是一个强大的工具,现在临时有一个删除某开源项目的单元测试模块的代码的需求.该项目单元测试在源代码用宏定义#define UNITTESTS打开条件编译.那么很简单的得到以下代码:
#!/bin/bash
for var in $(ls *.c *.h)
do
sed -n '/^[[:space:]]*#ifdef UNITTESTS/,/^[[:space:]]*#endif/p' ${var}
done
通过上面的脚本可以查看匹配的输出是否是你想要删除的代码片段.大致看了一下,刚好是自己要删除的代码.好的,开工,将正式删除代码.将上诉代码中的sed改成如下形式:
sed -i '/^[[:space:]]*#ifdef UNITTESTS/,/^[[:space:]]*#endif/d' ${var}
再一统计源代码的行数,ok,代码的四分之一的量没有了.对于看懂这个工程又有点把握了.
但是,在仔细审查代码的时候却发现上诉表达式在大多数的时候的表达是正常的,它可以删除如下形式的片段.
//xxxx.c or xxxx.h
#ifdef UNITTESTS
......
......
#endif
但是在遇到以下形式的代码片段的时候会出现你不期望的处理方式.
//xxxx.c or xxxx.h
#ifdef UNITTESTS
.....
#ifdef win32
.....
#endif
...
#endif
原来的代码会在第一个#endif处停止,在这种情况下上诉处理方案就瞎了.为了解决这个问题我采取以下方式慢慢逼近问题的解决方案.
正常情况下,sed -n '/#ifdef UNITTESTS/,/#endif/p' xxxx.c
即可简单的输出该文件的条件编译块.至于这个命令是咋解释的我们是否可以控制呢.说干就干.在一个文件中写入以下内容并将文件命名为test.sed:
/#ifdef UNITTESES/{
:repeat
p
n
/#endif/ b end
b repeat
:end
p
}
通过sed -n -f test.sed xxxx.c就可以得到直接输sed -n '/#ifdef UNITTESTS/,/#endif/p' xxxx.c
得到同样的输出.这个test.sed就是一个sed脚本.简单的解释一下上诉脚本的工作原理.p打印模式空间的内容,n在禁止静默输出的情况下,只是起到了一个读入新的一行覆盖模式空间中原有的内容.上诉脚本的工作是这样的,首先定位到文件中的#ifdef UNITTESTS位置,然后输出模式空间中内容,用下一行的内容覆盖模式空间,如果匹配到模式#endif就跳转到end标记.否则,无条件跳转到标签repeat.
为了解决上诉出现的嵌套条件定义的情况,新建一个文件test1.sed文件并加入以下内容:
/[[:space:]]*#ifdef UNITTESTS/{
:repeat
p
n
/[[:space:]]*#endif/b end
/[[:space:]]*#ifdef/b sub-repeat
b repeat
:sub-repeat
p
n
/#endif/b repeat
b sub-repeat
:end
p}
最后在终端中键入以下内容输出匹配到的内容.
for var in $(ls *.c .h)
do
sed -n -f test1.sed ${var}
done
要删除代码将下面内容写到一个sed.sed的文件里面
/[[:space:]]*#ifdef UNITTESTS/{
=
:label
n
/[[:space:]]*#ifdef.*/{
:sub-label
/[[:space:]]*#endif.*/ b label
n
b sub-label
}
/[[:space:]]*#endif.*/{
=
d
}
b label
}
上面代码输出了所有的语法块的行标记,通过下面代码即可完成操作.for var in $(ls *.c *.h); do sed -n -f sed.sed ${var} ; done | sed -n '{N;s/\n\(.*\)$/ \1/gp;d}'}' >> line
最终,直接按照line里面的内容直接删除即可
补充以下,sed的兄弟awk的参考资料参考链接http://www.ibm.com/developerworks/cn/education/aix/au-gawk/