【问题标题】:Awk doesn't match all match all my entriesawk 不匹配所有匹配我的所有条目
【发布时间】:2015-10-14 20:27:15
【问题描述】:

我正在尝试制作“脚本”——本质上是一个 awk 命令——以在 .c 文件中提取 C 代码函数的原型,以自动生成头文件 .h。 我是 awk 的新手,所以我不了解所有详细信息。

这是源 .c 的示例:

dict_t dictup(dict_t d, const char * key, const char * newval)
{

  int i = dictlook(d, key);

  if (i == DICT_NOT_FOUND) {

    fprintf(stderr, "key \"%s\" doesn't exist.\n", key);
    dictdump(d);
  }
  else {

    strncpy(d.entry[i].val, newval, DICTENT_VALLENGTH);
  }

  return d;
}


dict_t* dictrm(dict_t* d, const char * key) {

  int i = dictlook(d, key);

  if (i == DICT_NOT_FOUND) {

    fprintf(stderr, "key \"%s\" doesn't exist.\n", key);
    dictdump(d);
  }
  else {
    d->entry[i] = d->entry[--d.size];
  }
  if ( ((float)d->size)/d.maxsise < 0.25 ) {
    d->maxsize /= 2; 
    d->entry = realloc(d->entry, d->maxsize*sizeof(dictent_t*));
  }

  return d;
}

以及我想要生成的内容:

dict_t dictup(dict_t d, const char * key, const char *newval); 
dict_t* dictrm(dict_t* d, const char * key);

我的带有完整正则表达式的命令如下所示:

 awk '/^[a-zA-Z*_]+[:space:]+[a-zA-Z*_]+[:space:]*\(.*?\)/{ print $0 }' dict3.c 

但我对此一无所知。 所以我试着挤压它,看看我能不能带来一些东西。 我试过这个:

awk '/^[a-zA-Z*_]+[:space:]+[a-zA-Z*_]+/{ print $0 }' dict3.c 

我明白了:

dictent_t* dictentcreate(const char * key, const char * val) 
dict_t* dictcreate() 
dict_t* dictadd(dict_t* d, const char * key, const char * val) 
dict_t dictup(dict_t d, const char * key, const char * newval) 
dict_t* dictrm(dict_t* d, const char * key) {

它是许多奇迹的源泉!

  • 为什么第一个正则表达式不起作用?
  • 为什么第二个捕获了一些声明,但不是全部?我向你保证,任何声明之前都没有空格。我猜它没有捕捉到代码的其他部分,比如变量声明,因为缩进。
  • 第三个问题,为什么它抓住了我只需要表达式的所有行?
  • 最后一个,如何在每个正则表达式的末尾添加;

【问题讨论】:

  • 尝试删除[:space:]+并添加`+`(空格+)
  • @EdMorton 我是这么认为的,但快速测试(因为我分心)表明这有所帮助,但这可能只是因为我没有注意并且其他事情都错了。

标签: regex awk header-files text-extraction


【解决方案1】:

注意:自从我写下这个答案后,这个问题发生了很大的变化。

[:space:] 替换为[[:space:]]

$ awk '/^[a-zA-Z*_]+[[:space:]]+[a-zA-Z*_]+[[:space:]]*[(].*?[)]/{ print $0 }' dict3.c
dictent_t* dictentcreate(const char * key, const char * val)  
dict_t* dictcreate() 
void dictdestroy(*dict_t d) 
void dictdump(dict_t *d) 
int dictlook(dict_t *d, const char * key) 
int dictget(char* s, dict_t *d, const char *key)
dict_t* dictadd(dict_t* d, const char * key, const char * val)
dict_t dictup(dict_t d, const char * key, const char *newval) 
dict_t* dictrm(dict_t* d, const char * key)

原因是[:space:] 将匹配任何字符:space。这不是你想要的。

你想要[[:space:]],它将匹配任何空格。

太阳/Solaris

本机 Sun/Solaris awk 是出了名的漏洞百出。如果您在该平台上,请尝试nawk/usr/xpg4/bin/awk/usr/xpg6/bin/awk

使用 sed

sed 可以使用非常相似的方法。这使用基于您的正则表达式:

$ sed -n '/^[a-zA-Z_*]\+[ \t]\+[a-zA-Z*]\+ *[(]/p' dict3.c
dictent_t* dictentcreate(const char * key, const char * val)  
dict_t* dictcreate() 
void dictdestroy(*dict_t d) 
void dictdump(dict_t *d) 
int dictlook(dict_t *d, const char * key) 
int dictget(char* s, dict_t *d, const char *key)
dict_t* dictadd(dict_t* d, const char * key, const char * val)
dict_t dictup(dict_t d, const char * key, const char *newval) 
dict_t* dictrm(dict_t* d, const char * key)

-n 选项告诉 sed 不要打印,除非我们明确要求它打印。构造 /.../p 告诉 sed 如果斜线内的正则表达式匹配,则打印该行。

Ed Morton 建议的对正则表达式的所有改进也适用于此。

使用 perl

上面也可以采用perl:

perl -ne  'print if /^[a-zA-Z_*]+[ \t]+[a-zA-Z*]+ *[(]/' dict3.c

【讨论】:

  • 如果您确定您的文件只有空白和 no 选项卡,那么使用真正的空白就可以了。如果您不确定,请使用[[:blank:]][[:space:]]。后两者也是 unicode 安全的。
  • @mklement0 是的,您完全正确:ASCII \x20 是“空格”,而[:blank:] 表示空格或制表符。但是,[:space:] 表示任何空格或制表符或换行符或换行符或垂直制表符或换页符。如果我要设计一个具有教育意义的命名系统,那就不会了。
  • @John1024:好点:[:space:] 名字不好;也许应该是[:whitespace:]。在散文中,“空白”(所有空白)、“空白”(空格和/或制表符)和“空格”(仅限\x20)这三位一体对我来说很有意义。
  • @NicolasScottoDiPerto 获得一个几乎 POSIX 的 awk,其中包括支持 [[:space:]] 等字符类,在 Solaris 上使用 /usr/xpg4/bin/awk,而不是 nawk,而且绝对不旧,损坏的 awk (/usr/bin/awk)。尽管它的名称是“New awk”,但 nawk 实际上是一个非常古老的 awk,功能有限。那里的教训 - 在命名你的软件时永远不要使用“新”这个词!
  • @NicolasScottoDiPerto 您只是选择了错误版本的 awk 和 sed。如果您认为学习 perl 来进行这样的琐碎文本操作是合理的,那么您就偏离了轨道并且将浪费大量时间 - 只需使用标准 UNIX 工具的当前版本即可。您一直处于劣势,因为 Solaris 附带了非常旧版本的 sed 和 awk 作为默认版本。
【解决方案2】:

您尝试编写的正则表达式是:

$ awk '/^[[:alpha:]_][[:alnum:]_]*\**[[:space:]]+[[:alpha:]_][[:alnum:]_]*[[:space:]]*\([^)]*\)/' file
dictent_t* dictentcreate(const char * key, const char * val)
dict_t* dictcreate()
void dictdestroy(*dict_t d)
void dictdump(dict_t *d)
int dictlook(dict_t *d, const char * key)
int dictget(char* s, dict_t *d, const char *key)
dict_t* dictadd(dict_t* d, const char * key, const char * val)
dict_t dictup(dict_t d, const char * key, const char *newval)
dict_t* dictrm(dict_t* d, const char * key)

在没有字符类的情况下编写并假设您的语言环境将是:

$ awk '/^[a-zA-Z_][a-zA-Z0-9_]*\**[ \t]+[a-zA-Z_][a-zA-Z0-9_]*[ \t]*\([^)]*\)/' file
dictent_t* dictentcreate(const char * key, const char * val)
dict_t* dictcreate()
void dictdestroy(*dict_t d)
void dictdump(dict_t *d)
int dictlook(dict_t *d, const char * key)
int dictget(char* s, dict_t *d, const char *key)
dict_t* dictadd(dict_t* d, const char * key, const char * val)
dict_t dictup(dict_t d, const char * key, const char *newval)
dict_t* dictrm(dict_t* d, const char * key)

但是:

  1. 获取/使用具有字符类的 awk,因为如果没有字符类,那么谁知道它还缺少什么?
  2. 编写脚本来查找您想要的字符串总是微不足道的,但很难找到您不想要的字符串。例如,上面的内容将匹配 cmets 中的文本,并且在给出类似 int foo(int x /* always &gt; 0 (I hope) */) 的声明时会失败。在提供示例输入/输出时,您应该始终包含一些您认为脚本难以选择的文本,因为它“看起来”很像您想要选择的文本,但在错误的上下文中满足您的需求。李>

请注意,C 符号不能以数字开头,因此匹配数字的正则表达式不是[[:alnum:]_]+,而是[[:alpha:]_][[:alnum:]_]*。函数也可以并且经常返回指向指针的指针,* 可以在函数名称而不是函数返回类型旁边,所以你真的应该使用这样的正则表达式(未经测试,因为你没有提供输入这将匹配的格式)如果您的函数声明可以是任何正常格式:

awk '/^[[:alpha:]_][[:alnum:]_]*((\*[[:space:]]*)*|(\*[[:space:]]*)*|[[:space:]]+)[[:alpha:]_][[:alnum:]_]*[[:space:]]*\([^)]*\)/' file

这当然不会匹配跨行的声明 - 那是完全不同的蠕虫。

一般来说,你不能在没有 C 解析器的情况下解析 C,但如果你想要便宜又愉快的东西,那么至少首先在代码上运行一个 C 美化器,以尝试将所有各种可能的布局变成一种一致的格式(谷歌“ C 美化器”,您还需要去除 cmets(例如参见 https://stackoverflow.com/a/13062682/1745001)。

鉴于您的新要求和新示例输入/输出,这就是您的要求:

$ awk 'match($0,/^[[:alpha:]_][[:alnum:]_]*\**[[:space:]]+[[:alpha:]_][[:alnum:]_]*[[:space:]]*\([^)]*\)/) { print substr($0,RSTART,RLENGTH) ";" }' file
dict_t dictup(dict_t d, const char * key, const char * newval);
dict_t* dictrm(dict_t* d, const char * key);

但是,考虑到 C 代码的一般布局,这绝不是稳健的。你需要一个 C 解析器、一个 C 美化器和/或一个专门的工具来稳健地完成这项工作(例如 googl cscope)。

【讨论】:

  • 这是个好主意,我一定要试试。现在在 perl 中! ^^
  • Ed 在此处 (+1) 提供的关于正则表达式的许多好建议将转移到 perl。
  • @NicolasScottoDiPerto 为什么,哦,为什么,您想使用 perl 进行文本操作? awk 中的脚本太易读?太便携了?此外,您的主题说“Awk ...”并且您用 awk 而不是 perl 标记了您的问题 - 特别要求我们帮助您提出一个 awk 解决方案,然后说“In perl now”充其量是烦人的。
  • 是的,对不起,但我更多的是关于提取函数声明的目的,而不是关于 awk。我在一篇文章中告诉我,我无法在我的大学访问另一个版本的 awk,因为我主要想在那里提取文本,所以我不能使用 awk 来执行此操作......
  • 好的,谢谢你的建议,我去看看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-20
  • 2016-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
相关资源
最近更新 更多