awk / etc。:从文件中提取匹配项

时间:2009-06-12 01:47:10

标签: regex awk text-processing

我有一个HTML文件,想要在<li></li>代码之间提取文字。当然有一百万种方法可以做到这一点,但我认为在简单的shell命令中更多地养成这样做的习惯会很有用:

awk '/<li[^>]+><a[^>]+>([^>]+)<\/a>/m' cities.html

问题是,这打印所有而我只是想在括号中打印匹配 - ([^>]+) - 要么awk不支持,要么我不称职。后者似乎更有可能。如果要将提供的正则表达式应用于文件并仅提取指定的匹配项,您将如何进行?我已经知道了其他六种方式,但我不想让awk赢得这一轮;)

编辑:数据结构不合理,因此使用位置匹配($1, $2, etc.)是不行的。

5 个答案:

答案 0 :(得分:2)

如果你想在一般情况下这样做,你的列表标签可以包含任何合法的HTML标记,那么awk是错误的工具。这项工作的正确工具将是一个HTML解析器,您可以信任该解析器以获得HTML解析的所有细节,包括HTML和格式错误的HTML的变体。

如果您针对特殊情况执行此操作,您可以在其中控制HTML格式,那么您可以使awk为您工作。例如,假设您可以保证每个列表元素永远不会占用多行,始终在同一行上以</li>终止,从不包含任何标记(例如包含列表的列表),然后您可以使用awk来执行此操作,但是您需要编写一个完整的awk程序,该程序首先查找包含列表元素的行,然后使用其他awk命令来查找您感兴趣的子字符串英寸

但总的来说,awk是这项工作的错误工具。

答案 1 :(得分:1)

gawk -F'<li>' -v RS='</li>' 'RT{print $NF}' file

对我来说工作得很好。

答案 2 :(得分:0)

我看到了几个问题:

  • 该模式具有尾随'm',这对于Perl中的多行匹配很重要,但是Awk不使用与Perl兼容的正则表达式。 (至少,标准(非GNU)awk没有。)
  • 忽略这一点,该模式似乎会搜索“开始列表项”,然后搜索“<a>”到“</a>”,而不是结束列表项。
  • 您搜索任何非>'作为锚点主体的内容;这不是自动错误,但搜索任何不是“<”或任何不是“$1”的内容可能更为常见。
  • Awk不进行多行搜索。
  • 在Awk中,“nawk”表示第一个字段,其中字段由字段分隔符分隔,默认为空格。
  • 在经典sed & awk中(如1991年'{{1}}'书中所记载的那样)没有适当的机制来从匹配等中提取子字段。

目前尚不清楚Awk是否适合这项工作。实际上,正则表达式是这项工作的正确工具并不完全清楚。

答案 3 :(得分:0)

真的不知道awk,Perl怎么样?

tr -d '\012' the.html | perl \
-e '$text = <>;' -e 'while ( length( $text) > 0)' \
-e '{ $text =~ /<li>(.*?)<\/li>(.*)/; $target = $1; $text = $2; print "$target\n" }'

1)从文件中删除换行符,穿过perl

2)使用完整文本初始化变量,开始循环直到文本消失

3)对列表项标签所限定的内容进行“非贪婪”匹配,保存并打印目标,设置为下一遍

有意义吗? (警告,我自己没试过这个代码,需要很快回家......)

P.S。 - “perl -n”是awk(nawk?)模式。 Perl在很大程度上是Awk的超集,所以我从不费心去学习Awk。

答案 4 :(得分:0)

通过您的脚本,如果您可以得到您想要的内容(这意味着<li><a>标记在一行中。);

$ cat test.html | awk 'sub(/<li[^>]*><a[^>]*>/,"")&&sub(/<\/a>.*/,"")'

$ cat test.html | gawk '/<li[^>]*><a[^>]*>(.*?)<\/a>.*/&&$0=gensub(/<li[^>]*><a[^>]*>(.*?)<\/a>.*/,"\\1", 1)'

第一个用于每个awk,第二个用于gnu awk。