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 Commented Jun 18, 2020 at 19:05
  • Can you please provide the expected XML output ? Commented Jun 18, 2020 at 19:08
  • 2
    Bash has next to nothing to do with solving this problem.
    – Jeff Schaller
    Commented Jun 18, 2020 at 19:11
  • Updated post to include desired outputs. This is done in a bash script Commented Jun 18, 2020 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
    Commented Jun 19, 2020 at 0:01

2 Answers 2

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. Commented Jun 19, 2020 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. Commented Jun 19, 2020 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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .