4

I am new to the world of sed/awk and regex in general and have been studying their usage, but have been floundering trying to meet my need:

I have an htm page that has a single line notice that needs to be updated with user inputted text (via shell script) between two comments acting as tags, for example:

<!--BeginNoticeMSG-->NOTICE: This is a notice<!--EndNoticeMSG-->

The user inputted text (stored in a variable, let's call it $NEWNOTICE) would then need to replace what's between the tags, so effectively:

<!--BeginNoticeMSG-->$NEWNOTICE<!--EndNoticeMSG-->

Which would get inserted into the htm file as (for example):

<!--BeginNoticeMSG-->This is a test notice<!--EndNoticeMSG-->

How can I identify and replace the text between the tags properly?

  • Related: stackoverflow.com/a/1732454/1655874 – user26112 May 29 '13 at 19:39
  • If you want to do that as an exercise in string manipulation, why not. However I would think that the proper way for this kind of task is to use an include, if possible. Apache does allow that: <!--#include virtual="path/name/noticefile" --> – babou May 29 '13 at 21:00
3

This is a (quite) basic recipe that will meet your need only as exactly specified:

#!/bin/bash
REPLACEWITH="Your replacement text here"
STARTTAG="BeginNoticeMSG"
ENDTAG="EndNoticeMSG"
sed -E "s/(<\!\-\-$STARTTAG\-\->)(.*)(<\!\-\-$ENDTAG\-\->)/\1$REPLACEWITH\3/" -i target_file.html

It will break in a number of different ways if the input is different, especially if the input "tag" is broken into multiple lines.

Using regular expressions isn't typically recommended to process HTML and XML (I realize this is just a comment), but... If your input is as reliable as hinted by in this post, something this simple may do the trick.

I this case I back-reference the parts of your tag as \1 and \3 (which correlate to the parenthetical items in the regex) to reduce the amount of text needed to type out the replacement.

Or without -E option, and without back references:

#!/bin/bash
REPLACEWITH="Text to replace with here"
STARTTAG="BeginNoticeMSG"
ENDTAG="EndNoticeMSG"
sed -e "s/<\!\-\-$STARTTAG\-\->.*<\!\-\-$ENDTAG\-\->/<\!\-\-$STARTTAG\-\->$REPLACEWITH<\!\-\-$ENDTAG\-\->/" -i target_file.html
  • Looks like I am using an older version of sed, it does not support the -E option (nor the equivalent -r), taking this option off altogether produces an error: "sed: command garbled" Thoughts? – FFFlow May 29 '13 at 19:59
  • sed -e "s/<\!\-\-$STARTTAG\-\->.*<\!\-\-$ENDTAG\-\->/<\!\-\-$STARTTAG\-\->$REPLACEWITH<\!\-\-$ENDTAG\-\->/" -i test.txt This doesn't require extended regex, and doesn't use the back reference. – dougBTV May 29 '13 at 20:01
  • Also edited the answer to add an option without -E – dougBTV May 29 '13 at 20:05
  • Almost there, this produced: <!--BeginNoticeMSG-->This is a test notice<..!--EndNoticeMSG--> somehow got a few extra dots in there. – FFFlow May 29 '13 at 20:15
  • Interesting! I have sed 4.2.1, see what you have with sed --version. Here's a log from my terminal showing it working in test: pasteall.org/42676/bash – dougBTV May 29 '13 at 20:19
1

Assuming you never have more than one notice on the same line (more precisely, that you never have more than one occurrence of <!--BeginNoticeMSG--> or of <!--EndNoticeMSG--> on the same line):

sed -e "s&\(<!--BeginNoticeMSG-->\).*\(<!--EndNoticeMSG-->\)&\1$NEWNOTICE\2&"

If the begin and end comments can vary, you can write a regular expression for them.

Note that this only works if you are sure that $NEWNOTICE does not contain \, & or newline, because otherwise these characters are interpreted as sed syntax.

To be robust with punctuation characters, use awk instead.

export NEWNOTICE
awk '{sub(/<!--BeginNoticeMSG-->.*<!--EndNoticeMSG-->/, "<!--BeginNoticeMSG-->" env[NEWNOTICE] "<!--EndNoticeMSG-->"); print}'

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.