【问题标题】:extract data sed or grep or awk from xml从 xml 中提取数据 sed 或 grep 或 awk
【发布时间】:2018-08-23 12:44:46
【问题描述】:

我有以下 xml。我需要将 IP 地址、协议和端口提取到具有相应列名的 CSV 文件中。

<rule family="ipv4">
<source address="10.XXX.XX.XX"/>
<port protocol="tcp" port="22"/>
<log prefix="ber_" level="warning">
<limit value="1/m"/>
</log>
<accept/>
</rule>
<rule family="ipv4">
<source address="10.XXX.XX.XXX"/>
<port protocol="udp" port="1025"/>
<log prefix="ber_" level="warning">
<limit value="1/m"/>
</log>
<accept/>

我可以像 grep -Eo "([0-9]{1,3}[\.]){3}[0-9]{1,3}" 这样使用 grep 或 sed 来 grep IP 地址或端口,但我需要它作为 CSV 文件中的列。

IPAddress 协议端口。实现这一目标的最佳方法是什么?

【问题讨论】:

  • 使用 XPath 获取值。在 Linux 上,您可以使用 xmllint。
  • 您通常无法使用正则表达式解析 XML,本网站已讨论了数百次。如果您想要在输入格式稍有变化时不会失败的健壮代码,您必须使用 XML 解析器来加载 DOM,或使用 XPath 评估器。 Perl 或 Python 都有你需要的一切。
  • 请提供 REAL xml
  • 给新人的建议:如果一个答案解决了您的问题,请点击旁边的大复选标记 (✓) 接受它,也可以选择投票(投票至少需要 15 声望)点)。如果您发现其他答案有帮助,请给他们投票。接受和投票有助于未来的读者。请看the relevant help-center article

标签: xml csv awk sed grep


【解决方案1】:

不要使用正则表达式来解析 html/xml,而是一个真正的解析器(使用):

更正错误的输入xml文件:

<root>
    <rule family="ipv4">
        <source address="10.XXX.XX.XX"/>
        <port protocol="tcp" port="22"/>
        <log prefix="ber_" level="warning">
            <limit value="1/m"/>
        </log>
    </rule>
    <rule family="ipv4">
        <source address="10.XXX.XX.XXX"/>
        <port protocol="udp" port="1025"/>
        <log prefix="ber_" level="warning">
            <limit value="1/m"/>
        </log>
    </rule>
</root>

代码:

xmlstarlet sel -t -v '//source/@address | //port/@protocol | //port/@port' file |
perl -pe '$. % 3 != 0 && s/\n/,/g;END{print "\n"}'

输出:

10.XXX.XX.XX,tcp,22
10.XXX.XX.XXX,udp,1025

理论:

根据编译理论,不能使用基于finite state machine的正则表达式解析HTML。由于 HTML 的分层结构,您需要使用 pushdown automaton 并使用 YACC 之类的工具操作 LALR 语法。

中的 realLife©®™ 日常工具:

您可以使用以下方法之一:

xmllint

xmlstarlet

saxon-lint(我自己的项目)


检查:Using regular expressions with HTML tags

【讨论】:

  • 除此之外,我怎样才能进入列?
  • 不懂你的意思
  • 输出为 10.XXX.XX.XX tcp 22
【解决方案2】:

缺少xml工具,这里有一个脆弱的awk解决方案

1$ awk -v RS='</rule>' '
       {for(i=1;i<=NF;i++) 
          if($i~/^(address|protocol|port)/) 
            {split($i,a,"\""); printf "%s", a[2] (++c%3?FS:ORS)}}' file

10.XXX.XX.XX tcp 22
10.XXX.XX.XXX udp 1025

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-28
    • 2015-07-28
    • 2013-10-23
    • 1970-01-01
    • 2013-09-09
    • 2010-11-29
    • 1970-01-01
    • 2021-11-03
    相关资源
    最近更新 更多