I'm facing a rather newbie problem and caught my ears around it :( I have a file with xml tags and I want that when I find:


the </ac:image> one followed by an </a>, then to delete the line with </a>.

There was also an "a href" before but that one was rather specific and I deleted it with sed and a match. On this one I wonder how to approach it, if it's a multiline match but then not sure how to select the second line of the match.

Can someone point me in the right direction, please?

  • 4
    FYI, XML is impossible to parse with 100% reliability using regular expressions. This is a mathematical fact, not hyperbole. You might get "good enough for most purposes" but you will (provably) not be able to handle all valid XML input. See stackoverflow.com/a/8577108/5419599 for examples.
    – Wildcard
    Jul 30, 2019 at 5:55
  • 1
    taking a look now but this isn't really the case from that link. while this is xml, in reality what I want to replace always looks the same and there won't be any false positives with a regexp. it is in fact confluence page where initially I did copy paste of images from a locally hosted tiny web server. I was expecting confluence to add / upload images... it did links instead. Now I saw how a link to a locally uploaded attachment looks but I need to filter out what it added (the a href part and there is one more tag that I could already replace with sed).
    – Mihai
    Jul 30, 2019 at 7:38
  • If this is generated XML, then your reality is subject to change if any setting in Confluence changes.
    – muru
    Jul 30, 2019 at 7:39

2 Answers 2


If it is always the same sequence without surprises, you don't need XML parsing and you can indeed use sed.

To always check a pair of two lines, use the N;P;D pattern in sed:

sed 'N;P;\_</ac:image>\n *</a>$_d;D' filename

How does it work?

  • N appends the next line. Usually you do $!N to check whether there is a next line at all, but that's no problem in your case: If there is no next line, nothing special happens.
  • Now that you have two lines in your buffer, P prints the first line
  • \_</ac:image>\n *</a>$_d deletes the pattern space if you find the given pattern of two lines (the \n is the newline between the lines). The first line was already printed, so deleting the pattern space only drops the second line of the pair
  • The last statement is only executed if the pattern didn't match. The D deletes the first line (and the newline) and restarts the cycle with the second line of the pair, so each pair of lines is checked
gsed '/<\/ac:image>/,+1 { /<\/a>$/d; }' testfile.txt

where gsed stands for GNU sed.

  • Why not add a little explanation? Not everybody knows a range with +1 and not everybody understands that you need {} to use an address inside a range. Thank you.
    – Philippos
    Jul 31, 2019 at 7:09

You must log in to answer this question.

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