如何编写与#ifdef…#else…#endif C预处理器宏匹配的awk程序?
我有大量的C程序,它们有以下代码块如何编写与#ifdef…#else…#endif C预处理器宏匹配的awk程序?,c,awk,c-preprocessor,preprocessor,C,Awk,C Preprocessor,Preprocessor,我有大量的C程序,它们有以下代码块 100. #ifdef DEBUG1 . . . 102. #else . . . 105. #endif 或者 此外,单个文件可能包含多个#DEBUG宏。我想提取与宏对应的行号。假设代码段中的数字是源文件中的行号,我希望输出采用以下格式: FILE - MACRO_NAME - IFDEF - ELIF - ENDIF ----------------------------------------
100. #ifdef DEBUG1
.
.
.
102. #else
.
.
.
105. #endif
或者
此外,单个文件可能包含多个#DEBUG
宏。我想提取与宏对应的行号。假设代码段中的数字是源文件中的行号,我希望输出采用以下格式:
FILE - MACRO_NAME - IFDEF - ELIF - ENDIF
----------------------------------------
prog.c - DEBUG1 - 100 - 102 - 105
prog.c - DEBUG2 - 200 - X - 206
我如何编写一个awk
程序来实现同样的目标?如果awk
不是合适的工具,我可以使用什么工具?awk实际上有关联数组,因此我将采取以下方法:
#ifdef
(或类似变量,如#if 1
),增加一个变量,然后使用该变量存储if
行号,将else
和endif
行号设置为-1#else
行,使用当前变量设置else
行号#endif
,输出行号的任何详细信息,然后减小变量#elif
,您需要组合#else
和#if
操作,并确保相关的#end
关闭所有#if/#elif
行bash
脚本,展示了它是如何工作的:
#!/usr/bin/env bash
# Use this script as input file as well, luckily C preprocessor
# macros look like bash comments.
#ifdef XYZZY
# Some text inside the first ifdef
#if 0
# This is the inner bit.
#endif
#if 1
# blah blah blah
#elif defined TWISTY
# yada yada yada
#elif defined PASSAGES
# run out of phrases
#else
# still got nothing
#endif
#else
#ifdef PLUGH
# This is the plugh stuff
#else
# This is the anti-plugh stuff
#endif
#endif
awk <$0 '
$1 == "#ifdef" || $1 == "#if" {
level++
line_mac[level] = $0
gsub(/^[ \t]+/, "", line_mac[level])
line_if[level] = NR
line_else[level] = "X"
line_end[level] = "X"
typ_elif[level] = 0
next
}
$1 == "#elif" {
line_else[level] = NR
level++
line_mac[level] = $0
gsub(/^[ \t]+/, "", line_mac[level])
line_if[level] = NR
line_else[level] = "X"
line_end[level] = "X"
typ_elif[level] = 1
next
}
$1 == "#else" {
line_else[level] = NR
next
}
$1 == "#endif" {
while (typ_elif[level] == 1) {
printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
level--
}
printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
level--
}
'
Awk实际上具有关联数组,因此我将采用以下方法:
#ifdef
(或类似变量,如#if 1
),增加一个变量,然后使用该变量存储if
行号,将else
和endif
行号设置为-1#else
行,使用当前变量设置else
行号#endif
,输出行号的任何详细信息,然后减小变量#elif
,您需要组合#else
和#if
操作,并确保相关的#end
关闭所有#if/#elif
行bash
脚本,展示了它是如何工作的:
#!/usr/bin/env bash
# Use this script as input file as well, luckily C preprocessor
# macros look like bash comments.
#ifdef XYZZY
# Some text inside the first ifdef
#if 0
# This is the inner bit.
#endif
#if 1
# blah blah blah
#elif defined TWISTY
# yada yada yada
#elif defined PASSAGES
# run out of phrases
#else
# still got nothing
#endif
#else
#ifdef PLUGH
# This is the plugh stuff
#else
# This is the anti-plugh stuff
#endif
#endif
awk <$0 '
$1 == "#ifdef" || $1 == "#if" {
level++
line_mac[level] = $0
gsub(/^[ \t]+/, "", line_mac[level])
line_if[level] = NR
line_else[level] = "X"
line_end[level] = "X"
typ_elif[level] = 0
next
}
$1 == "#elif" {
line_else[level] = NR
level++
line_mac[level] = $0
gsub(/^[ \t]+/, "", line_mac[level])
line_if[level] = NR
line_else[level] = "X"
line_end[level] = "X"
typ_elif[level] = 1
next
}
$1 == "#else" {
line_else[level] = NR
next
}
$1 == "#endif" {
while (typ_elif[level] == 1) {
printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
level--
}
printf "if-line %-4s, else-line %-4s, endif-line %-4s, macro '%s'\n", line_if[level], line_else[level], NR, line_mac[level]
level--
}
'
来扩展@paxdiablo的答案。如果您有
gawk
并使用多个文件作为输入,则可以利用BEGINFILE
和ENDFILE
规则打印每个文件中的宏
注意,与只在所有输入的开头和结尾运行一次的BEGIN/END
块不同,这些块在每个文件的开头/结尾运行(不引人注意)
因此,一个简化的脚本,忽略#else
等,您可以添加额外的规则,下面这样的awk脚本可能对多个输入文件有用
#!/usr/bin/awk -f
BEGIN {
printf "%-10s | %-10s | %-5s | %-5s\n", "FILE", "MACRO", "IFDEF", "ENDIF";
print "----------------------------------------"
}
BEGINFILE {
delete macros;
delete locs;
i = 0;
}
/^[ \t]*#ifdef[\t ]+([^ \t])+/ {
macros[i++] = $2;
locs[i]["start"] = FNR;
}
/^[ \t]*#endif/ {
locs[--i]["end"] = FNR;
}
ENDFILE {
for (i = 0; i < length(macros); i++) {
printf "%-10s - %-10s - %-4d - %-4d\n",
FILENAME, macros[i], locs[i]["start"], locs[i]["end"];
}
}
来扩展@paxdiablo的答案。如果您有
gawk
并使用多个文件作为输入,则可以利用BEGINFILE
和ENDFILE
规则打印每个文件中的宏
注意,与只在所有输入的开头和结尾运行一次的BEGIN/END
块不同,这些块在每个文件的开头/结尾运行(不引人注意)
因此,一个简化的脚本,忽略#else
等,您可以添加额外的规则,下面这样的awk脚本可能对多个输入文件有用
#!/usr/bin/awk -f
BEGIN {
printf "%-10s | %-10s | %-5s | %-5s\n", "FILE", "MACRO", "IFDEF", "ENDIF";
print "----------------------------------------"
}
BEGINFILE {
delete macros;
delete locs;
i = 0;
}
/^[ \t]*#ifdef[\t ]+([^ \t])+/ {
macros[i++] = $2;
locs[i]["start"] = FNR;
}
/^[ \t]*#endif/ {
locs[--i]["end"] = FNR;
}
ENDFILE {
for (i = 0; i < length(macros); i++) {
printf "%-10s - %-10s - %-4d - %-4d\n",
FILENAME, macros[i], locs[i]["start"], locs[i]["end"];
}
}
给定这样的内容应该就是您所需要的(使用GNU awk作为ENDFILE和argid):
awk'
{hit=0}
$1==“#ifdef”{
宏名称=$2
计数[宏名称]++
命中率=1
}
$1~/#(else | endif)$/{hit=1}
hit{fnr[macroname,count[macroname],$1]=fnr;hit=0}
结束文件{
如果(ARGIND==1){
打印“文件”、“宏名称”、“IFDEF”、“ELIF”、“ENDIF”
}
for(计数中的宏名称){
对于(i=1;i给定这样的内容,您应该只需要(将GNU awk用于ENDFILE和argid):
awk'
{hit=0}
$1==“#ifdef”{
宏名称=$2
计数[宏名称]++
命中率=1
}
$1~/#(else | endif)$/{hit=1}
hit{fnr[macroname,count[macroname],$1]=fnr;hit=0}
结束文件{
如果(ARGIND==1){
打印“文件”、“宏名称”、“IFDEF”、“ELIF”、“ENDIF”
}
for(计数中的宏名称){
对于(i=1;i它需要多健壮?例如,它需要能够忽略字符串和注释中的#ELIF
语句吗?像#ifdef DEBUG1…#ifdef DEBUG2…#endif…#endif
?这样的嵌套定义如何?它不需要那么健壮。我很了解数据集。没有这种情况,即使它是这是非常罕见的。好吧,那么你的问题是至少包括一个简明的、可测试的样本输入文件和相关的预期输出的示例,这样我们就可以测试潜在的解决方案,以便我们可以帮助你。它需要有多强大?例如,它需要能够忽略内部的#ELIF
语句吗字符串和注释?嵌套定义如#ifdef DEBUG1…#ifdef DEBUG2…#endif…#endif
?它不需要那么健壮。我很了解数据集。没有这种情况,即使有,也非常罕见。那么您的问题是至少包含一个简洁、可测试的示例输入文件和相关的预期输出,这样我们就可以测试潜在的解决方案,从而帮助您。Ed,您可以在我的回答中抓住脚本的前26行进行测试,这至少会让您对它的工作有一定的信心。@paxdiablo谢谢,但您的示例涵盖了我在评论和在她的数据中,所以我的脚本无法处理。我尝试了你的脚本,试图更好地理解它(我的awk-foo很弱),但得到了稍微错误的结果。它看起来可能很简单
$ ./defs.awk tst.h tst2.h
FILE | MACRO | IFDEF | ENDIF
----------------------------------------
tst.h - DEBUG1 - 0 - 5
tst.h - INNER1 - 1 - 4
tst2.h - DEBUG2 - 0 - 3
awk '
{ hit = 0 }
$1 == "#ifdef" {
macroname = $2
count[macroname]++
hit = 1
}
$1 ~ /#(else|endif)$/ { hit = 1 }
hit { fnr[macroname,count[macroname],$1] = FNR; hit = 0 }
ENDFILE {
if (ARGIND == 1) {
print "FILE", "MACRO_NAME", "IFDEF", "ELIF", "ENDIF"
}
for (macroname in count) {
for (i=1; i<=count[macroname]; i++) {
print FILENAME, macroname, fnr[macroname,i,"#ifdef"]+0, fnr[macroname,i,"#elif"]+0, fnr[macroname,i,"#endif"]+0
}
}
delete count
delete fnr
}
' *.c