我正在试图从一个源文件中创build一个具有所有函数/枚举/结构/ etc名称的文件。 为此,我正在尝试使用sed
来完成这样的事情:
(原始文件)
function add1 (int i) { return i+1; }
(sed的输出)
function add1 (int i) { }
换句话说,我想删除函数正文的实际内容。 我到目前为止还没有得到它的工作。 有什么build议么?
编辑 :我尝试了这样的事情,没有成功(现在我试图只在函数的身体空白行):
sed '/{/,/}/ s/.*//'
而不是sed
,你总是可以在每字符字段模式( FS=""
)中使用awk
:
awk 'BEGIN { RS = "\n" ; FS = "" ; d = 0 ; } { for (i=1; i<=NF; i++) if ($i == "{") { d++ ; if (d == 1) printf "{\n" } else if ($i == "}") { d-- ; if (d == 0) printf "}" } else if (d == 0) printf "%s", $i ; if (d == 0) printf "\n" }' INPUT-FILE(s)...
以上将跳过任何成对花括号的内容,即函数和结构体,数组初始化等,并将结果输出到标准输出。 您可以指定一个或多个文件。 (如果你没有指定任何文件,它会期望来自标准输入的输入。)
就像现在一样,它会在引号或注释中混淆大括号。 这可以用相同的方法解决,但是它确实变得很复杂。 这只是一个黑客让你大部分的方式。
我添加了分号( ;
),以便您可以将所有内容填充到上面的代码片段中。
脚本的逻辑非常简单。 它使用空字段分隔符( FS
),以便输入中的每个字符都是它们自己的字段。 BEGIN
规则在处理任何输入之前运行一次,并设置它。 对于开发人员信息,我也初始化d = 0
尽管awk不是必需的,因为它假定未初始化的变量为空或零。 它将跟踪每个输入字符的当前支撑深度。
每个记录将执行第二个括号表达式。 由于我设置了RS = "\n"
,每行都是一个单独的表达式。 因此,它将在每个输入行执行一次。 由于FS = ""
,该行上的每个字符将是一个单独的字段。 记录中有NF
字段: $1
, $2
,.., $(NF-1)
和$NF
。 三部分if子句只是输出最外面的大括号,而不是大括号内的所有内容(即当d == 0
)。
可以扩展这个awk
scriptlet来包含注释,字符串,字符常量(使用\047
来引用单引号,除非你用#!/usr/bin/awk -f
把脚本放入单独的文件中),处理或忽略预处理器宏。
它确实有些复杂,最终你会得到几百行的awk脚本,但是它应该是相当可靠和相当快的。 这是可能的原因是因为在这种情况下C中的标记化规则很容易遵循; 我个人会在所有其他使用情况下使用全面的C词法分析器(词法分析器或扫描仪)。 也可能是为了这个。
如果你想使用一个成熟的C词法分析器,网络上可以自由地使用它们,但是你必须使用像C或C ++这样的更高级的语言。 如果你想处理所有的角落案例,它也需要加入一个C / C ++预处理器,但是这些规则很容易(即使使用awk)。
在一致格式的文件上,你可以做类似的事情
sed '/{$/ {:r;/\n}/!{N;br}; s/\n.*\n/\n/}'
立即读取函数体并删除大括号之间的所有内容:
$ echo 'function add1 (int i) { if (i == 1) {return i+1;} }' | sed '/{$/ {:r;/\n}/!{N;br}; s/\n.*\n/\n/}' function add1 (int i) { }
该命令仅适用于直接在换行符之后以{
直接在前,以a结尾}
块。
在:r;/\n}/!{N;br}
部分:r
定义了一个名为r
的标签 ,其中另一行从输入( N
)附加到模式空间,然后执行流程开始再次( br
)。 它只发生在\n}
遇到。 所以当我们离开这个“循环”的时候,我们在模式空间中有了整个函数体,然后我们使用s
命令。
我会首先建议确保您的C源文件正确缩进。 你可以使用indent -gnu
。
那么你可以使用一些sed
技巧。 使用适当缩进的代码,您只需要关心大括号(打开或关闭)作为其行的第一个字符。
我不确定你为什么要这样做。 特别是, struct
可以是,有时甚至是嵌套的。 还有病理性的情况 – 例如用大括号定义的东西的预处理宏等等。
一个更好的方法可能是在编译器内部进行操作(但是你必须处理来自#include
-d头文件的东西)。 你可以使用MELT来达到这个目的(MELT是扩展GCC的高层领域特定语言,并且正在GCC内部工作)。