-3
I have an xml that looks something like this


<?xml version="1.0" encoding="UTF-8"?>
<project
    xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <version>1</version>
    </parent>
    <version>5</version>
    <properties>
        <test.version>10</test.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <version>${test.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

My task is to

  1. change only the version that is there inside parent tag
  2. change test.version inside properties tag.

My resulting xml should look like

<parent>
    <version>2</version>                 //changed here
</parent>

<version>5<version>

<properties>
    <test.version>20</test.version>     //changed here
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <version>${test.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

I have tried this using sed for the second requirement.

sed -i '/<test.version>/,/<\/test.version>/s/10/20/' "filename"

assuming all the xml is there in a file

The problem with the above sed command is that it is able to replace 10 with 20. but i want to replace any number with 20. how to do this.

for the first requirement when i am trying to use the same sed command as above all the matching version is being changed. i want only version inside parent to be changed. Again here i want to change anything inside parent/version to say 30

Cyrus
  • 84,225
  • 14
  • 89
  • 153
Abhishek
  • 650
  • 1
  • 8
  • 31
  • 4
    Please [Don't Parse XML/HTML With Regex.](https://stackoverflow.com/a/1732454/3776858). I suggest to use an XML/HTML parser (xmlstarlet, xmllint ...). – Cyrus Jan 01 '22 at 21:43
  • @Cyrus the problem is can't install any additional command where my shell script is working. I need to use existing command like sed only to manipulate xml – Abhishek Jan 01 '22 at 21:46
  • When you have a valid XML, please click here, and start reading some questions and answers: https://stackoverflow.com/questions/tagged/xmlstarlet – Luuk Jan 01 '22 at 21:46
  • This can be done with awk but only when formatting is never changed. If formatting is **guaranteed** to never change you can continue to post complete XML and describe how you decide to input values and if they are dependent. – konsolebox Jan 01 '22 at 21:56
  • _"Some people, when confronted with a problem, think_ “I know, I'll use regular expressions.” _Now they have two problems."_ -- [Jamie Zawinski](http://www.jwz.org/) – Jim Garrison Jan 02 '22 at 02:03
  • 1
    _"looks something like this"_ -- aye, there's the rub. Regex is the wrong tool, and will break unexpectedly when the format changes slightly. – Jim Garrison Jan 02 '22 at 02:05
  • 1
    Do you have `xsltproc` installed, it's based on the very standard `libxml` library? How about Python? – Zach Young Jan 02 '22 at 04:34

3 Answers3

1

Use Perl and its XML parser to update nodes if you can not install xmlstarlet.

Put my parser in a file named xmlupdate.pl:

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

my $key = $ARGV[0];
my $value = $ARGV[1];
my $file= $ARGV[2];

my $parser = XML::LibXML->new();
my $cyrus = $parser->parse_file($file);

for my $data ( $cyrus->findnodes($key . '/text()') ) {
  $data->setData($value);
}

print $cyrus->toString;

Syntax: perl xmlupdate.pl XML-path new_value filename

xmlupdate.pl writes its output to stdout.


Then use this to update //project/parent/version to value 2 from file file.xml:

perl xmlupdate.pl '//*[name()="project"]/*[name()="parent"]/*[name()="version"]' '2' 'file.xml' > file_tmp.xml
mv file_tmp.xml file.xml

And update //project//properties/test.version to value 20:

perl file.pl '//*[name()="project"]/*[name()="properties"]/*[name()="test.version"]' '20' 'file.xml' > file_tmp.xml
mv file_tmp.xml file.xml

Hint: Your file uses namespaces. Without namespaces you could just use //project/parent/version and //project//properties/test.version.

Cyrus
  • 84,225
  • 14
  • 89
  • 153
-2

Using GNU Awk:

gawk -v val=2 -v RS="</project>" 'match($0, /(.*<parent>\s*<version>\s*)[0-9]*(\s*<\/version>.*)/, a) { t = a[1] val a[2] } END { if (t) print t RS > FILENAME }' file.xml
gawk -v val=20 -v RS="</project>" 'match($0, /(.*<properties>\s*<test\.version>\s*)[0-9]*(\s*<\/test\.version>.*)/, a) { t = a[1] val a[2] } END { if (t) print t RS > FILENAME }' file.xml
konsolebox
  • 72,135
  • 12
  • 99
  • 105
-2

If we assume line numbers will not change for the tags:

Parent's version line number = 6

Test version's line number = 10

Single line:

sed -i "6s/[0-9]/<new_parent_value>/;10s/[0-9][0-9]/<new_test_value>/" file.xml

Individual lines:

sed -i "6s/[0-9]/<new_parent_value>/" file.xml
sed -i "10s/[0-9][0-9]/<new_test_value>/" file.xml

Write the expected values instead of <new_parent_value> and <new_test_value>

If using script with inputs suits you:

#!/bin/bash

# take user input for new values
echo "Enter new parent version value:"
read parverval
echo "Enter new test version value:"
read tesverval

# find the lines
pver=`grep -n "<parent>" file.xml | cut -d: -f1`
((pver=$pver+1))
tver=`grep -n "<test.version>" file.xml | cut -d: -f1`

# make the changes
echo "Changing parent version to $parverval "
sed -i "${pver}s/[0-9]/$parverval/p" file.xml

echo "Changing test version value to $tesverval "
sed -i "${tver}s/[0-9][0-9]/$tesverval/p" file.xml

This will take input for the new values.

If you don't want to take inputs:

#!/bin/bash

parverval="<new_parent_value>"
tesverval="<new_test_value>" 
# find the lines
pver=`grep -n "<parent>" file.xml | cut -d: -f1`
((pver=$pver+1))
tver=`grep -n "<test.version>" file.xml | cut -d: -f1`

sed -i "${pver}s/[0-9]/$parverval/p" file.xml
sed -i "${tver}s/[0-9][0-9]/$tesverval/p" file.xml

Considerations are

-Keeping the "<parent>" and its "<version>" tags serial.

-Number of digits

ust
  • 161
  • 8