Solved

Perl Script to append xml file

Posted on 2013-11-27
32
198 Views
Last Modified: 2015-04-23
I have an xml file like below.

 <interface type='network'>
      <mac address='52:54:00:a3:24:30'/>
      <source network='phybrid'/>
      <target dev='vnet3'/>
      <alias name='net1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
    </interface>

I want a perl script which adds below lines to this

<filterref filter='clean-traffic'>
        <parameter name='IP' value='1.2.3.1'/>
        <parameter name='IP' value='3.2.3.3'/>
      </filterref>

So the final output should be like below

<interface type='network'>
      <mac address='52:54:00:fc:70:00'/>
      <source network='phybrid'/>
      <target dev='vnet3'/>
      <filterref filter='clean-traffic'>
        <parameter name='IP' value='1.2.3.1'/>
        <parameter name='IP' value='3.2.3.3'/>
      </filterref>
      <alias name='net1'/>
      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
    </interface>
0
Comment
Question by:pehalwaan
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 15
  • 11
  • 5
32 Comments
 
LVL 26

Assisted Solution

by:wilcoxon
wilcoxon earned 333 total points
ID: 39681638
The simplest (possibly naive version) would be:
$xml =~ s{(<target dev=.*?/>)$}{$1\n    <filterref filter='clean-traffic'>\n        <parameter name='IP' value='1.2.3.1'/>\n        <parameter name='IP' value='3.2.3.3'/>\n    </filterref>}msg;

Open in new window

0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39681665
This is not working. Can you please give me a script which accepts xml file as an argument

I would really appreciate it
0
 
LVL 26

Expert Comment

by:wilcoxon
ID: 39681763
What's not working?  Here's an alternate way to do it as a one-liner command...
perl -pe -i.bak 's{(<target dev=.*?/>)$}{$1\n    <filterref filter=\'clean-traffic\'>\n        <parameter name=\'IP\' value=\'1.2.3.1\'/>\n        <parameter name=\'IP\' value=\'3.2.3.3\'/>\n    </filterref>}' <filename>

Open in new window


One side questions - why does your XML use single quotes around attribute values?  Both are valid but double quotes is the norm in XML.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 3

Author Comment

by:pehalwaan
ID: 39681787
KVM
This is what i get while running. These are KVM xml files. These are auto-generated when we create kvm's.

I am unable to work with one liners. Can you provide a detailed script.
0
 
LVL 84

Expert Comment

by:ozo
ID: 39681802
perl -i.bak -pe "s#(\s*)(?=<alias name)#\$1<filterref filter='clean-traffic'>\n\$1 <parameter name='IP' value='1.2.3.1'/>\n\$1 <parameter name='IP' value='3.2.3.3'/>\n\$1</filterref>#" file.xml
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39681824
There are many other occurrences of aliasname which are getting replaced too in the file.

What i am trying out is parsing the xml file and appending my new lines in interface tag.

Please suggest if you have some better approach
0
 
LVL 84

Expert Comment

by:ozo
ID: 39681844
perl -i.bak -pe "s#(\s*)(?=<alias name='net1'/>)#\$1<filterref filter='clean-traffic'>\n\$1 <parameter name='IP' value='1.2.3.1'/>\n\$1 <parameter name='IP' value='3.2.3.3'/>\n\$1</filterref>#" file.xml
0
 
LVL 26

Expert Comment

by:wilcoxon
ID: 39681861
Sigh.  I forgot the -i has to go before the -e.  Try this one-liner.  If there are other instances of "alias name" and "target dev", you will very likely have to do as you suggest (and actually parse the XML).
perl -i.bak -pe "s{(<target dev=.*?/>)$}{$1\n    <filterref filter='clean-traffic'>\n        <parameter name='IP' value='1.2.3.1'/>\n        <parameter name='IP' value='3.2.3.3'/>\n    </filterref>}" test1.xml

Open in new window

0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39681874
These one liners are wonderful and work perfectly. But can you help me with the script because IP values coming in this will be not fixed , they will be coming as arguments in my production environment
0
 
LVL 84

Expert Comment

by:ozo
ID: 39681880
perl  -MO=Deparse -i.bak -pe "s#(\s*)(?=<alias name='net1'/>)#\$1<filterref filter='clean-traffic'>\n\$1 <parameter name='IP' value='1.2.3.1'/>\n\$1 <parameter name='IP' value='3.2.3.3'/>\n\$1</filterref>#"

BEGIN { $^I = ".bak"; }
LINE: while (defined($_ = <ARGV>)) {
    s[(\s*)(?=<alias name='net1'/>)][$1<filterref filter='clean-traffic'>\n$1 <parameter name='IP' value='1.2.3.1'/>\n$1 <parameter name='IP' value='3.2.3.3'/>\n$1</filterref>];
}
continue {
    die "-p destination: $!\n" unless print $_;
}
0
 
LVL 26

Assisted Solution

by:wilcoxon
wilcoxon earned 333 total points
ID: 39681940
For a script version, try this...
use strict;
use warnings;
use Tie::File; # not strictly necessary but I like it when editing files
my $fil = shift or die "Usage: $0 file ip1 ... ipX\n";
tie my @lines, 'Tie::File', $fil or die "could not open $fil: $!";
# may need to restructure next line
# can't remember if @lines gets recalced each iteration or not
for my $i (0..@lines-1) {
    if ($lines[$i] =~ m{^(\s+)<target dev=.*?/>\s*$}) {
        my $spc = $1;
        splice @lines, $i+1, 0, "$spc<filterref filter='clean-traffic'>", map({ "$spc    <parameter name='IP' value='$_'/> } @ARGV), "$spc</filterref>";
    }
}
untie @lines;

Open in new window

0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39683489
When using this one liner inside my perl script it gives below error

String found where operator expected at /usr/local/sbin/KVM3.pm line 414, near "n"    /etc/libvirt/qemu/$cust_id-$server_id.xml""
syntax error at /usr/local/sbin/KVM3.pm line 414, near ""perl -i.bak -pe "s#(\s*)(?=<source network='pyhbrid'/>)#\$1<filterref filter='clean-traffic'>\n\$1 <parameter name='IP' value='$wan_primary_ip'/>\n\$1</filterref>#"

Can you help write a simple formatted code for this. This is very urgent
0
 
LVL 84

Expert Comment

by:ozo
ID: 39683555
this is how a one liner can be converted into a script
perl -MO=Deparse  -Mwarnings -Mstrict -i.bak -pe "s#(\s*)(?=<source network='pyhbrid'/>)#\$1<filterref filter='clean-traffic'>\n\$1 <parameter name='IP' value='$wan_primary_ip'/>\n\$1</filterref>#"
BEGIN { $^I = ".bak"; }
use warnings;
use strict;
LINE: while (defined($_ = <ARGV>)) {
    s[(\s*)(?=<source network='pyhbrid'/>)][$1<filterref filter='clean-traffic'>\n$1 <parameter name='IP' value=''/>\n$1</filterref>];
}
continue {
    die "-p destination: $!\n" unless print $_;
}
-e syntax OK
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39684256
I have written a script to change root password directly in shadow file. Below it is

my $admin_password=shift;
my $password_salt=qx{perl -e 'print crypt("$admin_password","\$6\$saltsalt\$") . "\n"'};
print "$password_salt";
qx{perl -pe 's|(root):(\$.*?:)|\1:\$6\$SALTsalt\$$password_salt:|' shadow > shadow.new};

When i run these perl commands from command line, they work fine, but when i write them in a pl file and run pl file, i get below errors.

perl p.pl dfr
Final $ should be \$ or $name at -e line 1, within string
syntax error at -e line 1, near ","$6$saltsalt$""
Execution of -e aborted due to compilation errors.

I am unable to stat what is wrong here. Can anybody help
0
 
LVL 84

Expert Comment

by:ozo
ID: 39684265
Did you mean
my $password_salt=qx{perl -e 'print crypt("$admin_password","\$6\$saltsalt\\\$") . "\n"'};        
Or easier
my $password_salt=crypt($admin_password,"$6$saltsalt\$")."\n";
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39684279
easier method is working but not replacing new salt in shadow file
0
 
LVL 84

Expert Comment

by:ozo
ID: 39684291
Neither $6 nor $SALTsalt seems to be defined in
qx{perl -pe 's|(root):(\$.*?:)|\1:\$6\$SALTsalt\$$password_salt:|' shadow > shadow.new};
and it doesn't replace anything in shadow file, it would replace in shadow.new file
and are you sure that qr|(root):($.*?:)| matches in shadow file?
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39684293
below is pattern in shadow.new file

root:$6$izkFWw9QZFNE7Y6B$5LgqIJPO1LT9WTq/DgThqzuYNq8QmOwpkVnC0aWdU727kdljidRXe1WVT2wlqn1RS6MLCOMEjTSbVzC5YTBYa1:16007:0:99999:7:::

And i didn't get your point of $6 and $SALT
0
 
LVL 84

Expert Comment

by:ozo
ID: 39684301
Did you want
{local @ARGV=("shadow");
local $^I=".old";
s/(?:^root:)[^:]/$saltsalt$password_salt/ and print while <>;
}
0
 
LVL 84

Expert Comment

by:ozo
ID: 39684304
Since $6 and $SALTsalt are in "" quotes, the -e script will try to interpolate it.
Did you mean to say \\\$6$saltsalt
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39684315
No. My just motive is to change root password by replacing "hashed password " in shadow file. As i mentioned, if i run these one liners from command line, they work completely fine.

Can you help me get the correct script
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39684358
Any suggestions/answers
0
 
LVL 84

Expert Comment

by:ozo
ID: 39685052
I made a couple of suggestions.  If you find them unsuitable, I am unsure what kinds of improvements you would require.
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39686168
Please help me fix this

SCRIPT

#!/usr/bin/perl

my $admin_password=shift;
my $password_salt=qx{openssl passwd -1 $admin_password};    ##### Generate
print "$password_salt";
$old_password_salt=qx{cat shadow.new | grep root | cut -d: -f2};
print "$old_password_salt";
qx{sed -i -e 's|$old_password_salt|$password_salt|g' shadow.new};

OUTPUT

perl p.pl bghuin
$1$tOuKv7mH$HKHlphoXl4LTSRUXWqWvM.
$6$izkFWw9QZFNE7Y6B$5LgqIJPO1LT9WTq/DgThqzuYNq8QmOwpkVnC0aWdU727kdljidRXe1WVT2wlqn1RS6MLCOMEjTSbVzC5YTBYa1
sed: -e expression #1, char 108: unterminated `s' command

PLEASE HELP.
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39686184
PLEASE REPLY
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 39686214
Please HELP
0
 
LVL 84

Expert Comment

by:ozo
ID: 39686305
chomp $password_salt;
chomp $old_password_salt;
0
 
LVL 84

Accepted Solution

by:
ozo earned 167 total points
ID: 39686400
Or

#!/usr/bin/perl
my $admin_password=shift;
my $password_salt=qx{openssl passwd -1 $admin_password};    ##### Generate
print $password_salt;
chomp  $password_salt;
@ARGV=("shadow.new");
$^I="";
while( <> ){
    s/:([^:]*):/:$password_salt:/ && print STDERR "$1\n" if /root/;
    print;
}
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 40740472
I've requested that this question be deleted for the following reason:

Solved it
0
 
LVL 26

Expert Comment

by:wilcoxon
ID: 40740473
ozo and I both provided significant help on this question.  The person asking often would just reply with things like "help" without any details about what was not working.  The asker never responded with what was not working in either my final script or ozo's final script.  As such, it seems like at least the majority of the points should be distributed.
0
 
LVL 3

Author Comment

by:pehalwaan
ID: 40740647
@All-

I am sorry for the action. One of my team members deleted it. I am accepting the solution.
0

Featured Post

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In the distant past (last year) I hacked together a little toy that would allow a couple of Manager types to query, preview, and extract data from a number of MongoDB instances, to their tool of choice: Excel (http://dilbert.com/strips/comic/2007-08…
Checking the Alert Log in AWS RDS Oracle can be a pain through their user interface.  I made a script to download the Alert Log, look for errors, and email me the trace files.  In this article I'll describe what I did and share my script.
Learn several ways to interact with files and get file information from the bash shell. ls lists the contents of a directory: Using the -a flag displays hidden files: Using the -l flag formats the output in a long list: The file command gives us mor…
In a recent question (https://www.experts-exchange.com/questions/29004105/Run-AutoHotkey-script-directly-from-Notepad.html) here at Experts Exchange, a member asked how to run an AutoHotkey script (.AHK) directly from Notepad++ (aka NPP). This video…

626 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question