0

I am deleting sections of tags from an XML and need to find the end tag to stop at.

Example XML

<SearchPattern> 
     <something></something>
</SearchPattern>

<OtherTag></OtherTag>

<SearchPatternHit2>
    <something></something>
</SearchPatternHit2>

Example XML Desired Output after iteration 1

<OtherTag></OtherTag>

<SearchPatternHit2>
    <something></something>
</SearchPatternHit2>

Example XML Desired Output after iteration 2

<OtherTag></OtherTag>

My current attempt uses sed to find the first occurrence line number like this:

start_line = $(grep -n "<${SEARCH_PATTERN}" ${FILE_PATH} | head -1| cut -f1 -d':')

The output for this is only the line number integer.

I then try to get the line of the closing tag using:

finish_line = $(sed -n "${start_line},$ !d;/<\/${LEADING_TAG}>/=" ${FILE_PATH} | head -1)

I believe the result of finish line is incorrect when multiple instances of the search pattern exist, so it does not get the first closing tag.

In the example, the pattern is found on line 1, and 7. on the first pass, start_line=1, but finish_line is not returning 3 as it should.

After getting these two values, I call a simple sed statement that is working.

sed -i "${start_line}, ${finish_line}d" ${FILE_PATH}

What would be a better way to obtain the line of the closing tag from each block?

5
  • 3
    Editing XML/HTML require a proper XML parser – Gilles Quenot Jun 18 '20 at 19:05
  • Can you please provide the expected XML output ? – Gilles Quenot Jun 18 '20 at 19:08
  • 2
    Bash has next to nothing to do with solving this problem. – Jeff Schaller Jun 18 '20 at 19:11
  • Updated post to include desired outputs. This is done in a bash script – Enter Strandman Jun 18 '20 at 19:17
  • 1
    Sed could be called from sh or zsh or tcsh and it wouldn't make a difference; that's why I suggested removing the bash tag. – Jeff Schaller Jun 19 '20 at 0:01
3

The correct way to edit XML is to use an XML parser, as this will handle a change in presention format such as additional spaces or different line layout. My currently preferred one is xmlstarlet.

Given a valid XML document as /tmp/xml

<?xml version="1.0"?>
<root>
  <SearchPattern>
    <something/>
  </SearchPattern>

  <OtherTag/>

  <SearchPatternHit2>
    <something/>
  </SearchPatternHit2>

  <SearchPatternHit2>
    <something_else/>
  </SearchPatternHit2>
</root>

You can delete the <searchPattern/> section very simply

xmlstarlet edit -d '//SearchPattern' /tmp/xml

To remove one occurence of a repeated element, include an array reference (based from 1)

xmlstarlet edit -d '//SearchPatternHit2[1]' /tmp/xml
2
  • this will not work for me, I am unable to put other tools on the system. Having to use a work around. – Enter Strandman Jun 19 '20 at 14:43
  • @EnterStrandman if the system is administered separately, then put in a Change Request for the xmlstarlet tool to be installed. It's available as a standard package on most Linux-based distributions, and heavily documented by IBM so probably available there too. – roaima Jun 19 '20 at 18:37
-2

I found a simple solution, it's kind of hacky, but I cannot put other tools on the system so here's my work around.

A universal comment was added before and after each piece put into the XML by my script, which could then be found with grep and easily removed with the same simple sed statement.

New XML

1. <!-- START MY CONFIG ADDITIONS-->
2. <SearchPattern> 
2.      <something></something>
3. </SearchPattern>
4. <!-- END MY CONFIG ADDITIONS-- 
5.
6. <OtherTag></OtherTag>
7.
8. <!-- START MY CONFIG ADDITIONS--> 
9. <SearchPatternHit2>
10.     <something></something>
11. </SearchPatternHit2>
12. <!-- END MY CONFIG ADDITIONS-->

New Vars

start_line=$(grep -n "<!-- START MY CONFIG ADDITIONS-->" ${FILE_PATH} | head -1 | cut -f1 -d':')
finish_line=$(grep -n "<!-- END MY CONFIG ADDITIONS-->" ${FILE_PATH} | head -1 | cut -f1 -d':')

Code

count=$(grep -c "<!-- START MY CONFIG ADDITIONS-->" ${FILE_PATH})

while [ $count -gt 0 ]; do
    start_line=$(grep -n "<!-- START MY CONFIG ADDITIONS-->" ${FILE_PATH} | head -1 | cut -f1 -d':')
    finish_line=$(grep -n "<!-- END MY CONFIG ADDITIONS-->" ${FILE_PATH} | head -1 | cut -f1 -d':')

    sed -i "${start_line}, ${finish_line}d" $FILE_PATH

    ((count--))
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.