0

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

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

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

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

4

5 回答 5

2

如果您想在一般情况下执行此操作,您的列表标签可以包含任何合法的 HTML 标记,那么awk是错误的工具。适合这项工作的工具是 HTML 解析器,您可以相信它可以正确获取 HTML 解析的所有小细节,包括 HTML 的变体和格式错误的 HTML。

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

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

于 2009-06-12T02:07:39.257 回答
1
gawk -F'<li>' -v RS='</li>' 'RT{print $NF}' file

对我来说工作得很好。

于 2010-07-13T16:54:16.723 回答
0

我看到了几个问题:

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

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

于 2009-06-12T02:00:08.557 回答
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) 对由列表项标签限制的内容进行“非贪婪”匹配,保存并打印目标,为下一次通过设置

有道理?(警告,我自己没有尝试过这段代码,需要尽快回家......)

PS - “perl -n” 是 Awk (nawk?) 模式。Perl 在很大程度上是 Awk 的超集,所以我从来没有费心去学习 Awk。

于 2009-06-12T02:28:15.743 回答
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。

于 2009-06-12T16:17:57.420 回答