I want create simple xml file using bash script ( not with dom )

but when I set value in the line then echo print the $lorem word and not the val


echo '<root><foo a="b">$lorem</foo><bar value="ipsum" /></root>' >> $MY_XML

I also try this

echo '<root><foo a="b">\$lorem</foo><bar value="ipsum" /></root>' >> $MY_XML

echo '<root><foo a="b">"$lorem"</foo><bar value="ipsum" /></root>' >> $MY_XML

echo '<root><foo a="b">\"$lorem\"</foo><bar value="ipsum" /></root>' >> $MY_XML

but all these print the exactly the line and not the val

please advice how to print the val $lorem ? as the following example

 <root><foo a="b">LOL</foo><bar value="ipsum" /></root>

5 Answers 5


Print the line on this way:

echo '<root><foo a="b">'"$lorem"'</foo><bar value="ipsum" /></root>' >> "$MY_XML"

This is need because single quotes deny shell interpreter from replace environment variables inside


Use in-line document with cat

cat >destfile <<EOF
<your ZML here...where $vas expand happly>
but the rest remain as is

I used it in a lot of scripts. Consider also envsubst command to restrict variables you can use in the in-line document.


Frustratingly perhaps, one of the combinations you omitted would have worked:

echo "<root><foo a=\"b\">$lorem</foo><bar value=\"ipsum\" /></root>" >> $MY_XML

Single quotes leave everything exactly as you type it. Escaping with \ will not work. Double quotes will evaluate the string. Inside double quotes you can use \ to escape specific characters (including the double-quote character itself).

You could also combine parts of the string that are quoted in different ways. Here, $lorem remains double-quoted but the rest of the string is surrounded with single quotes:

echo '<root><foo a="b">'"$lorem"'</foo><bar value="ipsum" /></root>' >> $MY_XML

The issue is that you are using your variable in a single-quoted string. The shell will never expand variables in single quoted strings.

Another issue is that the value in $lorem would not be properly XML encoded, so a value like <!-- would mess up the XML document and make it unparseable.

Instead, use jo from https://jpmens.net/2016/03/05/a-shell-command-to-create-json-jo/ to create a JSON document and the yq tool from https://kislyuk.github.io/yq/ to convert this to XML:

$ lorem=LOL
$ jo -p 'foo[@a]'=b 'foo[#text]'="$lorem" 'bar[@value]'=ipsum
   "foo": {
      "@a": "b",
      "#text": "LOL"
   "bar": {
      "@value": "ipsum"

Then pass this through yq -x --xml-root=root . to convert to XML and wrap it in a top-level root node called root. Keys starting with @ will be turned into node attributes, and the value of the #text key will be turned into the node's value.

$ jo 'foo[@a]'=b 'foo[#text]'="$lorem" 'bar[@value]'=ipsum | yq -x --xml-root=root .
  <foo a="b">LOL</foo>
  <bar value="ipsum"></bar>

With the value <!-- in $lorem:

$ lorem='<!--'
$ jo 'foo[@a]'=b 'foo[#text]'="$lorem" 'bar[@value]'=ipsum | yq -x --xml-root=root .
  <foo a="b">&lt;!--</foo>
  <bar value="ipsum"></bar>

Or, with the literal value $lorem in the variable:

$ lorem='$lorem'
$ jo 'foo[@a]'=b 'foo[#text]'="$lorem" 'bar[@value]'=ipsum | yq -x --xml-root=root .
  <foo a="b">$lorem</foo>
  <bar value="ipsum"></bar>
  • yq --help print nothing about xml support (but mentioned commands works). The recommended way to use xml related functionality is via xq executable.
    – eNca
    Feb 2 at 8:37

When you interpolate values into XML template make sure to escape special characters:

escape() {
    local val
    printf %s "$val"

lorem="\"Foo\" & 'Bar' & <Baz>"

cat << EOF
<root><foo a="b">$(escape "$lorem")</foo><bar value="ipsum" /></root>
  • You can replace your escape function with xmlstarlet escape.
    – Kusalananda
    Jan 31, 2023 at 6:46

You must log in to answer this question.

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