jkitzmiller
jkitzmiller

Reputation: 37

Modify a string in an XML file while not changing an identical string

I'm writing a bash script, and I need it to make changes to an XML file, replacing a bunch of different servlet-name items with "disabledController".

I know I can do this using sed, however there are 2 different lines that have the same string, but only one needs to be changed.

For example, I need to change this:

<servlet>
        <servlet-name>CreateCertificateAuthorityBackup</servlet-name>
        <servlet-class>com.company.ca.CABackup</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>CreateCertificateAuthorityBackup</servlet-name>
        <url-pattern>createCertificateAuthorityBackup</url-pattern>
    </servlet-mapping>

Into this:

<servlet>
        <servlet-name>CreateCertificateAuthorityBackup</servlet-name>
        <servlet-class>com.company.ca.CABackup</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>disabledControler</servlet-name>
        <url-pattern>createCertificateAuthorityBackup</url-pattern>
    </servlet-mapping>

If I use sed like so:

sed "s@<servlet-name>CreateCertificateAuthorityBackup.*@<servlet-name>disabledController</servlet-name>@"

It replaces both servlet-name items instead of just the 2nd one. Is there a way to do what I want to do here?

Upvotes: 1

Views: 148

Answers (2)

that other guy
that other guy

Reputation: 123550

Use an XML tool to modify XML. This makes the solution much more robust and standards compliant than treating it as text. Replacement won't fail if the file is reformatted according to XML rules, and won't break if other XML standard tools operate on the same file.

You can use xmlstarlet to replace the servlet-mapping whose servlet-name value is CreateCertificateAuthorityBackup like this:

xmlstarlet edit -L \
  -u '/web-app/servlet-mapping/servlet-name[normalize-space(text())="CreateCertificateAuthorityBackup"]' \
  -v "disabledController"  web.xml

given a web.xml like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
  <servlet>
    <servlet-name>CreateCertificateAuthorityBackup</servlet-name>
    <servlet-class>com.company.ca.CABackup</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CreateCertificateAuthorityBackup</servlet-name>
    <url-pattern>createCertificateAuthorityBackup</url-pattern>
  </servlet-mapping>
</web-app>

Upvotes: 0

Steve
Steve

Reputation: 54502

To replace just the second occurrence, use nesting and pattern ranges beginning from the start of the file:

sed -i '0,/CreateCertificateAuthorityBackup/! { 0,//s//disabledController/ }' file

Or in full:

sed -i '0,/CreateCertificateAuthorityBackup/! { 0,/CreateCertificateAuthorityBackup/s/CreateCertificateAuthorityBackup/disabledController/ }' file

Explanation of the longhand command:

0,/CreateCertificateAuthorityBackup/      # is a pattern range from the start of
                                          # the file to the first occurrence of:
                                          # 'CreateCertificateAuthorityBackup'

! { ... }                                 # if not in the above pattern range, do
                                          # every between the braces.

0,/CreateCertificateAuthorityBackup/      # match between the beginning of the
                                          # file again and the next occurrence
                                          # of 'CreateCertificateAuthorityBackup'

s/ ... / ... /                            # perform the necessary substitution

If you'd like to replace the third occurrence, just add another layer of nesting:

sed '0,/CreateCertificateAuthorityBackup/! { 0,//! { 0,// s//disabledController/ } }' file

EDIT:

To make changes between the 'servlet-mapping' tags, all you need is a single pattern range:

sed '/<servlet-mapping>/,/<\/servlet-mapping>/s/CreateCertificateAuthorityBackup/disabledController/' file

You may like to read more about pattern ranges here. HTH.

Upvotes: 1

Related Questions