It seems like you want to
- remove the text
an example;
from the value of the /c/a
node in the XML document, and
- add a sub-node to the
/c/a
node called b
, with the value a test;
.
You can easily do this with xmlstarlet
in the shell:
xmlstarlet ed -u '/c/a' -x 'substring-before(text(), "an example;")' file.xml |
xmlstarlet ed -s '/c/a' -t elem -n 'b' -v 'a test;'
The first invocation of xmlstarlet
on the example document in the question would result in the following output, where some text is removed from the /c/a
node's value:
<?xml version="1.0"?>
<c>This is an example. <a>This is </a></c>
The second invocation takes this modified document and produces the following by introducing the /c/a/b
node:
<?xml version="1.0"?>
<c>This is an example. <a>This is <b>a test;</b></a></c>
The xmlstarlet
invocations may be combined into a single command. Below, I've used the long options and also use --inplace
for in-place editing of the original document (this is for illustration only, you should run without --inplace
to determine the transformations works first):
xmlstarlet ed --inplace \
--update '/c/a' -x 'substring-before(text(), "an example;")' \
--subnode '/c/a' -t elem -n 'b' -v 'a test;' file.xml
A generalisation of the above to something that performs the two edits to any a
node containing the text an example;
(which is what was actually requested in the question):
xmlstarlet ed \
--var paths '//a[contains(text(), "an example;")]' \
--update '$paths' -x 'substring-before(text(), "an example;")' \
--subnode '$paths' -t elem -n 'b' -v 'a test;' file.xml
The only new thing here is that we first store the paths for all the nodes we want to edit in the internal variable $paths
. We then refer to these paths in the --update
and --subnode
modifications.
<a>This is *an example*</a>
needs to be changed to<a>This is </a>
with an embedded element<d>a test</d>
. The XSLT came from fpmurphy's answer to the cross-referenced question.