使用Perl有条件地将标记添加到XML文件

时间:2015-10-29 22:45:50

标签: xml perl xml-twig

我不是一个开发人员,而是一个网络安全人员。如果元素尚不存在,我需要插入一个标记和值。

这是我的XML文件的片段

<NessusClientData_V2> 
    <Policy> 
    ... 
    <Report> 
    ... 
        <ReportHost name="1.2.3.4"> 
        ... 
            <HostProperties> 
            ... 
                <tag name="HOST_END">Thu Sep 17 17:23:19 2015</tag> 
                <tag name="system-type">general-purpose</tag> 
                <tag name="operating-system">MS WIN</tag> 
                <tag name="ssh-auth-meth">password</tag> 
                <tag name="ssh-login-used">backup</tag> 
                <tag name="local-checks-proto">ssh</tag> 
                <tag name="host-ip">1.2.3.4</tag> 
                <tag name="HOST_START">Thu Sep 17 17:00:14 2015</tag>
            </HostProperties> 
            ...

我需要检查每个ReportHost元素并创建一个

<tag name="mac-address">

如果它还没有。

我的输出需要包含所有原始信息,但只需要添加到。

2 个答案:

答案 0 :(得分:0)

您可以使用XML::Twig执行此操作。以下代码迭代所有HostProperties个节点并检查它们是否存在tag name mac-address,如果不存在,则插入一个。

use strict;
use warnings;
use XML::Twig;

# at most one div will be loaded in memory
my $twig = XML::Twig->new(
    twig_handlers => {
        '//ReportHost/HostProperties' => sub {
            my $seen = 0;
            foreach my $elem ( $_->children('tag') ) {
                if ( $elem->atts->{name} eq 'mac-address' ) {
                    $seen++;
                    last;
                }
            }
            $_->insert_new_elt( 
              'last_child', 
              'tag' => { name => 'mac-address' }, 
              'barfoo' 
            ) unless $seen;
        }
    },
    pretty_print => 'indented',
);

$twig->parse( \*DATA );
$twig->flush;

__DATA__
<NessusClientData_V2>
    <Policy>
    <Report>
        <ReportHost name="1.2.3.4">
            <HostProperties>
                <tag name="HOST_END">Thu Sep 17 17:23:19 2015</tag>
                <tag name="system-type">general-purpose</tag>
                <tag name="operating-system">MS WIN</tag>
                <tag name="ssh-auth-meth">password</tag>
                <tag name="ssh-login-used">backup</tag>
                <tag name="local-checks-proto">ssh</tag>
                <tag name="host-ip">1.2.3.4</tag>
                <tag name="HOST_START">Thu Sep 17 17:00:14 2015</tag>
            </HostProperties>
        </ReportHost>
        <ReportHost name="127.0.0.1">
            <HostProperties>
                <tag name="HOST_END">Thu Sep 17 17:23:19 2015</tag>
                <tag name="system-type">general-purpose</tag>
                <tag name="operating-system">MS WIN</tag>
                <tag name="ssh-auth-meth">password</tag>
                <tag name="ssh-login-used">backup</tag>
                <tag name="local-checks-proto">ssh</tag>
                <tag name="host-ip">127.0.0.1</tag>
                <tag name="HOST_START">Thu Sep 17 17:00:14 2015</tag>
                <tag name="mac-address">foobar</tag>
            </HostProperties>
        </ReportHost>
    </Report>
</Policy>
</NessusClientData_V2>

您必须将->parse(\*DATA)替换为->parse_file,并可能添加一些随机的mac地址。

答案 1 :(得分:0)

好吧,我在perlmonks上回答,并且通过更多的XML示例,我将回收答案。对于这个技巧,我使用xpath来查询XML,而不是走树。这背后的原因是因为xpath允许您在单个语句中查询元素,属性和属性内容。

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->parsefile( 'your_file.xml' );

#search for "HostProperties" nodes within the tree. You can
#be more specific about this if you need to. 
foreach my $HP ( $twig->get_xpath('//HostProperties') ) {
    #check if it has a "tag" element with a "mac-address" "name" attribute.
    if ( not $HP->get_xpath('./tag[@name="mac-address"]') ) {
        #insert a new element at the end of the entry. 
        $HP->insert_new_elt( 'last_child', 
                     'tag', { 'name' => 'mac-address' },
                     "DE:AD:BE:EF:F0:FF" );
    }
}

#note - pretty printing helps reading, but might
#cause some problems with certain XML constructs. Shouldn't in your specific example though. 
$twig->set_pretty_print("indented_a");
$twig->print;

#to save it to a file:
open ( my $output, '>', 'processed.xml' ) or die $!;
print {$output} $twig -> sprint;
close ( $output );