Reputation: 37
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
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
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