I have a valid XML file. I would like to duplicate a tag with contents via linux shell command(s).
By duplication I mean: copy <x>...</x>
entirely, then paste right after the original.
I do not have any prior knowledge of the contents or structure inside of the tag being duplicated, but I do know XPath to that tag.
So far I have achieved required result with sed via regex. I would like to use appropriate tools like xmlstarlet or xmllint instead, tools from official debian repo are preferred.
It seems the task is achievable this way:
To me that procedure seems a little convoluted, is there any better way?
Example input XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<someVariedContents/>
</item>
</root>
Example output XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<someVariedContents/>
</item>
<item>
<someVariedContents/>
</item>
</root>
To duplicate an element using xmlstarlet edit
, for example:
# shellcheck shell=sh disable=SC2016
xmlstarlet edit \
--var F 'root/item[1]' \
-a '$F' -t 'elem' -n 'item' -v '' \
-u '$prev' -x '$F/node() | $F/@*' \
file.xml
F
variable refer to the source element (for brevity)-a
(aka --append
) inserts a new empty item
element just
after $F
--append
's -v (--value)
the -x (--expr)
clause
of the -u (--update)
option takes an XPath argument, hence
the two-step approach-x '$F/node() | $F/@*'
makes a deep copy of source's nodes on
the child and attribute axes
(using a union)
-- since item
doesn't actually have any attributes
| $F/@*
can be omittedIn an xmlstarlet edit
command
--var
defines a named variable, and the back reference $prev
variable (aka $xstar:prev
) refers to the node(s) created by the most
recent -s
, -i
, or -a
option which all define or redefine it
(see xmlstarlet.txt
for a few examples of --var
and $prev
).
To process a multi-member node-set rather than a single element you can use the `-x` option with a relative XPath expression; for an example see this.