我有一个像这样的一行文件巴斯德宾但要长得多。
我的目标是仅过滤
example1: start with <a
end with </a>
example2: start with PZ
end with的字符串部分s16
,因此在每种情况下都将文本保留在匹配之间,而不依赖于 html 实体
我已经有了一个FreeBSD
依赖 html 实体的解决方法
- 美化为多行
tidy -i -m -w 160 -ashtml -utf8 ~/file
- 如果不包含字符串则删除行
sed -i '' '/\<\/a\>/!d' ~/file
顺便说一下,我正在尝试运行直接过滤器而不依赖 html 实体。目前我只能得到匹配的确切开头,但我不知道我正在过滤的字符串内容有多长,所以我无法精确地获取匹配的结尾,请参阅意外结果步骤来重现
重现意外结果的步骤
wget -O ~/file https://pastebin.com/raw/xbti369J
grep -E -o ".{0,0}PZ.{0,46}" ~/file
因为我们要求固定长度,所以我们得到了错误的行
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16">A</t
目标是获得线条结果模式,无论长度如何,如下所示
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16
答案1
您想要使用 XML 解析器,例如xmllint
.
使用下面的 XPath 表达式过滤掉a
元素之间的文本:
xmllint --html --xpath '//a/text()' <file>
答案2
如果要选择从 aPZ
到最近的每个片段s16
,则需要非贪婪匹配,(扩展)正则表达式不支持这种匹配grep
,但 GNU对于 Perl 样式表达式grep
有erl 选项:-P
grep -P -o "PZ.*?s16" ~/file
Perl 表达式“.*?”代表使整个表达式匹配的任何字符的最短匹配。
这可能仍然不是您想要的,因为PZ
比赛内部还有更多内容,但据我了解您的示例,您只需要PZ
后面的那些,中间s16
没有其他内容。PZ
因此,让我们在第二步中删除不需要的东西:
grep -P -o "PZ.*?s16" ~/file | sed 's/.*PZ/PZ/'
答案3
有很多方法可以做到这一点。
1启用 PCRE 的 GNU grep。这里我们利用非贪婪正则表达式 *?与负前瞻一起丢弃 PZ 和 s16 之间发生的任何 PZ。
grep -Po 'PZ(?:(?!PZ).)*?s16' file
2 如果您无法访问这样的 grep 版本,您可以使用原始版本,即 Perl ,它将具有正则表达式支持。
perl -lne 'print for /PZ(?:(?!PZ).)*?s16/g' file
3 我们可以使用 sed 来做到这一点。在第一遍中,我们将 PZ 和 s16 标记为 BOL 和 EOL。然后将此修改后的输入传递给第二个 sed,该 sed 将选择以 PZ 开头、以 s16 结尾的行,并且内部不得包含 PZ。
< file \
sed 's/PZ/\n&/g;s/s16/&\n/g' |
sed '/^PZ.*s16$/!d;/..*PZ/d' |
cat
4 我们。在此仅使用一次 sed 调用。它需要 GNU sed。
sed '/\n/{
/^PZ[^\n]*s16/!D
s//&\n/;P;D;}
s/PZ/\n&/g;D
' file