I have to parse HTML to change text between two brackets that are not HTML code back to < or >.

Here an example of my HTML code that I have to replace:

<content:encoded><![CDATA[<div class="pre_headline">some text</div> <p>…. More text . </p><p></p><h2> More text </h2><p> More text < text between angle brackets > … more text
… </content:encoded>

desired output:

<content:encoded><![CDATA[<div class="pre_headline">some text</div> <p>…. More text . </p><p></p><h2> More text </h2><p> More text &lt; text between angle brackets &gt; … more text
… </content:encoded>

All the text is in one line. All the replacement I'm doing now is with sed or awk. But I couldn't figure out a way to replace the brackets in text without changing all the html tags.

I thought to define that all html brackets are not followed by a space. the inline text brackets are usually followed by a space. This could be a way to choose which brackets i have to replace. Maybe there is a better rule, since this method would not replace text in brackets without space :(

The following sed command would replace all brackets.

sed -e 's/>/\&gt;/g' | 
sed -e 's/</\&lt;/g' |
  • 9
    Use an XML parser (xmlstarlet, xmllint, ...).
    – Cyrus
    Sep 27, 2015 at 9:55
  • 2
    This will be impossible to do unless you can find a way to define which < are good and which ones aren't. If they don't always have spaces around them, I don't see how anyone (including a human) can know which < > to change and which to leave.
    – terdon
    Sep 27, 2015 at 10:24

1 Answer 1


It is possible with sed but rather difficult then any XML parsers.

sed '
#puts open and close tag in one pattern
#mark pairable tags by `#` symbol
s/\(.*<\)\(\([^#> ]*\).*<\)\/\3/\1#\2#\/\3/
#other variant
#change non-marked text
#remove marks
' file.html
  • This doesn't work on the OP's example. It also changes the < of the <p> which is right after the </h2> and it doesn't change the < of < text between. I don't understand how you define "pairable" tags, the target tags are also paired. Also, if I understand correctly, this will fail for unpaired tags, such as <br>, which should be left alone. That is impressive sed-fu though.
    – terdon
    Sep 27, 2015 at 13:35
  • @terdon The OP example is broken because its miss </p> close to end and </!> after <![CDATA… In the case XML parsers get error too. *Paired tag* I mean started with same name in <>` with / e.g. <p></p>, <div ...></div>, <content:encoded></content:encoded>. The unpaired tags should be operated separatelly by s/<\(br\)>/<#\1/g
    – Costas
    Sep 27, 2015 at 13:48
  • Yeah, but you can't expect HTML to always have </p> tags. This is probably the best possible answer without a dedicated parser but it still fails :(
    – terdon
    Sep 27, 2015 at 14:36

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.