题
我有一个 HTML 文件,想提取之间的文本 <li>
和 </li>
标签。当然有一百万种方法可以做到这一点,但我认为更多地养成在简单的 shell 命令中执行此操作的习惯会很有用:
awk '/<li[^>]+><a[^>]+>([^>]+)<\/a>/m' cities.html
问题是,这打印 一切 而我只是想打印括号中的匹配项—— ([^>]+)
--要么awk不支持这个,要么我无能。后者的可能性似乎更大。如果您想将提供的正则表达式应用于文件并仅提取指定的匹配项,您会怎么做?我已经知道六种其他方法,但我不想让 awk
赢得这一轮;)
编辑:数据结构不佳,因此使用位置匹配($1, $2, etc.
)是不行的。
解决方案
这是你的脚本,如果你能得到你想要的东西(这意味着<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。
其他提示
如果你想这样做,在一般情况下,你的清单标签可以包含任何合法的HTML标记,然后awk
是错误的工具。这项工作的合适的工具将是一个HTML解析器,你可以信任得到正确所有的HTML解析的小细节,包括HTML和畸形HTML的变体。
如果您是一个特例,在那里你可以控制HTML格式这样做,那么你可能能够使awk
为你工作。例如,假设你能保证每个列表元素从未占据多于一行,总是与在同一行</li>
终止,决不会包含任何标记(如包含列表的列表),那么你可以使用awk
来做到这一点,但你需要编写第一查找包含列表元素线整体awk
程序,然后使用其他awk
命令,找到刚才子你感兴趣的内容。
但在一般情况下,awk
是这个职位的错误的工具。
gawk -F'<li>' -v RS='</li>' 'RT{print $NF}' file
工作得很好我。
我发现有几个问题:
- 该模式有一个尾随“m”,这对于 Perl 中的多行匹配很重要,但 Awk 不使用与 Perl 兼容的正则表达式。(至少,标准(非 GNU)awk 没有。)
- 忽略这一点,该模式似乎搜索“开始列表项”,后跟锚点“
<a>
' 到 '</a>
',而不是最终列表项。 - 您搜索任何不是 '
>
' 作为锚点的主体;这并不是自动错误的,但搜索任何不是 ' 的内容可能更常见<
',或者任何两者都不是的东西。 - awk 不进行多行搜索。
- 在 awk 中,'
$1
' 表示第一个字段,其中字段由字段分隔符分隔,默认为空格。 - 经典中
nawk
(如记录在'sed & awk
' 书籍年份 1991)没有适当的机制将子字段从比赛中拉出,等等。
目前尚不清楚 Awk 是否适合这项工作。事实上,尚不完全清楚正则表达式是否适合这项工作。
真的不知道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中。