Reputation: 38005
Question for the Bash ninjas: I have an XML template file that has some template markers in the form {{VARIABLE_NAME}}
and want to find/replace all occurences of that marker with the contents of a text file.
So imagine template.xml
looks something like this:
<?xml>
<root>
<description>
{{DESCRIPTION}}
</description>
</root>
And I have a text file called description.txt
that could potentially contain any characters (let's assume it's already xml-safe for simplicity). I want to replace the marker with the contents of the file, supporting newlines and other characters.
Previously I've been doing this in my code like this (which works great for simple strings):
sed -i '' -e "s/{{DESCRIPTION}}/$DESCRIPTION/g" template.xml
However, if $DESCRIPTION
contains newlines, sed balks:
sed: 1: "s/{{DESCRIPTION}}/Hello w ...": unescaped newline inside substitute pattern
Is there a better way to do this, or should I first search/replace all newlines with escaped variants in the $DESCRIPTION
variable first. And are there any other characters I need to escape, like /
?
Upvotes: 3
Views: 1489
Reputation: 38005
Thanks for the answers. Always interested in learning new ways to do things in Bash. I ended up solving it in a different way, however, by escaping the string using Bash's built-in replace notation, which I just discovered (sed
failed at this for reasons I'm still not quite clear on):
DESCRIPTION=`cat description.txt`
DESCRIPTION=${DESCRIPTION//$'\n'/\\$'\n'} # escape newlines
DESCRIPTION=${DESCRIPTION//\//\\\/} # escape slashes
DESCRIPTION=${INPUT//$'&'/\\$'&'amp;} # escape ampersands
# other xml-escaping...
...and then using sed
to actually do the replacement inline in output.xml:
cp -f template.xml output.xml
sed -i '' -e "s/{{DESCRIPTION}}/$DESCRIPTION/g" output.xml
Since the newlines and slashes are now escaped (am I missing anything??) the sed command accepts the replacement string without complaint and it renders the output file perfectly.
Upvotes: 2
Reputation: 113844
Here is a BASH (sh, actually) script to do the replacement:
#!/bin/sh
while IFS= read -r line
do
case "$line" in
*{{DESCRIPTION}})
cat "description.txt"
;;
*)
echo "$line"
;;
esac
done <"template.xml"
In the problem statement, "{{DESCRIPTION}}" appears on a line by itself. If that is not the case in general, some minor changes would be needed to preserved the other data on the line.
Upvotes: 2
Reputation: 195059
use awk:
awk -v RS='\0' 'NR==FNR{r=$0;next}{sub(/\{\{DESCRIPTION\}\}/,r)}7' desc temp.xml
I think you know how to play with redirection to save the output into a file.
test: in the example, file f
contains many "special" chars
kent$ cat f.xml
<?xml>
<root>
<description>
{{DESCRIPTION}}
</description>
</root>
kent$ cat f
foo
'
"
O"'
/
\
-
bar
kent$ awk -v RS='\0' 'NR==FNR{r=$0;next}{sub(/\{\{DESCRIPTION\}\}/,r)}7' f f.xml
<?xml>
<root>
<description>
foo
'
"
O"'
/
\
-
bar
</description>
</root>
Upvotes: 1