I have just learned about xmlstarlet, but unfortunately I have a really hard time with XML, so I hope I'll get some help with this ...
Say, I have this XML file, test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <g id="layer3" inkscape:label="hello">
    <circle id="circ2" inkscape:label="there"/>
    <rect id="rect2" inkscape:label="world"/>
  </g>
  <g id="layer4">
    <circle id="circ3" inkscape:label="more"/>
  </g>
</objects>
So what I want to do is: for each node where inkscape:label attribute exists, copy value of the inkscape:label attribute to the id attribute; so the expected output from the above would be:
<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <g id="hello" inkscape:label="hello">
    <circle id="there" inkscape:label="there"/>
    <rect id="world" inkscape:label="world"/>
  </g>
  <g id="layer4">
    <circle id="more" inkscape:label="more"/>
  </g>
</objects>
How can I do this with xmlstarlet?
Apparently I can replace all id attributes with a fixed value by using expression string("TEST") like this:
$  xmlstarlet edit -N inkscape="http://www.inkscape.org/namespaces/inkscape" --update '//*/@id' --expr 'string("TEST")'
test.xml
test.xml:3.40: Namespace prefix inkscape for label on g is not defined
  <g id="layer3" inkscape:label="hello">
                                       ^
test.xml:4.46: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ2" inkscape:label="there"/>
                                             ^
test.xml:5.44: Namespace prefix inkscape for label on rect is not defined
    <rect id="rect2" inkscape:label="world"/>
                                           ^
test.xml:8.45: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ3" inkscape:label="more"/>
                                            ^
<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <g id="TEST" inkscape:label="hello">
    <circle id="TEST" inkscape:label="there"/>
    <rect id="TEST" inkscape:label="world"/>
  </g>
  <g id="TEST">
    <circle id="TEST" inkscape:label="more"/>
  </g>
</objects>
... and I can "reinsert" the value of the attribute id with expression string(../@id) like this (so I basically get same output as input):
$ xmlstarlet edit -N inkscape="http://www.inkscape.org/namespaces/inkscape" --update '//*/@id' --expr 'string(../@id)' test.xml
test.xml:3.40: Namespace prefix inkscape for label on g is not defined
  <g id="layer3" inkscape:label="hello">
                                       ^
test.xml:4.46: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ2" inkscape:label="there"/>
                                             ^
test.xml:5.44: Namespace prefix inkscape for label on rect is not defined
    <rect id="rect2" inkscape:label="world"/>
                                           ^
test.xml:8.45: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ3" inkscape:label="more"/>
                                            ^
<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <g id="layer3" inkscape:label="hello">
    <circle id="circ2" inkscape:label="there"/>
    <rect id="rect2" inkscape:label="world"/>
  </g>
  <g id="layer4">
    <circle id="circ3" inkscape:label="more"/>
  </g>
</objects>
... but I cannot use the same trick (expression string(../@inkscape:label) - or string(../@*[local-name()='label']) as per How does local-name find attributes in an xml node?) to read from attribute inkscape:label - and I cannot really tell whether that is because of the "Namespace prefix" .. "not defined" message:
$ xmlstarlet edit -N inkscape="http://www.inkscape.org/namespaces/inkscape" --update '//*/@id' --expr 'string(../@inkscape:label)' test.xml
test.xml:3.40: Namespace prefix inkscape for label on g is not defined
  <g id="layer3" inkscape:label="hello">
                                       ^
test.xml:4.46: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ2" inkscape:label="there"/>
                                             ^
test.xml:5.44: Namespace prefix inkscape for label on rect is not defined
    <rect id="rect2" inkscape:label="world"/>
                                           ^
test.xml:8.45: Namespace prefix inkscape for label on circle is not defined
    <circle id="circ3" inkscape:label="more"/>
                                            ^
<?xml version="1.0" encoding="UTF-8"?>
<objects>
  <g id="" inkscape:label="hello">
    <circle id="" inkscape:label="there"/>
    <rect id="" inkscape:label="world"/>
  </g>
  <g id="">
    <circle id="" inkscape:label="more"/>
  </g>
</objects>
And via get attribute value using xmlstarlet or xmllint ; I can confirm I can target the id attribute with:
xmlstarlet select -N inkscape="http://www.inkscape.org/namespaces/inkscape" --template --value-of '//*/@id' test.xml
... but the corresponding command for the inkscape:label returns nothing:
xmlstarlet select -N inkscape="http://www.inkscape.org/namespaces/inkscape" --template --value-of '//*/@inkscape:label' test.xml
It's probably that namespace thing, but I don't understand how can I ignore the namespace, and just relate to the attribute names in the document as they are ...
EDIT: finally solved the issue here with Python 3:
#!/usr/bin/env python3
# https://stackoverflow.com/questions/30097949/elementtree-findall-to-recursively-select-all-child-elements
# https://stackoverflow.com/questions/13372604/python-elementtree-parsing-unbound-prefix-error
# https://stackoverflow.com/questions/2352840/parsing-broken-xml-with-lxml-etree-iterparse
# https://stackoverflow.com/questions/28813876/how-do-i-get-pythons-elementtree-to-pretty-print-to-an-xml-file
import sys
import lxml
import lxml.etree
import xml.etree.ElementTree as ET
def proc_node(node):
  target_label = 'inkscape:label' # file without namespace, like `test.xml` here
  #target_label = '{http://www.inkscape.org/namespaces/inkscape}label' # file with namespace (like proper Inkscape .svg)
  if target_label in node.attrib:
    node.attrib['id'] = node.attrib[target_label]
  for childel in node.getchildren():
    proc_node(childel)
parser1 = lxml.etree.XMLParser(encoding="utf-8", recover=True)
tree1 = ET.parse('test.xml', parser1)
ET.indent(tree1, space="  ", level=0)
proc_node(tree1.getroot())
print(lxml.etree.tostring(tree1.getroot(), xml_declaration=True, pretty_print=True, encoding='UTF-8').decode('utf-8'))
... if I call this xmlproc.py, then the result is:
$ python3 xmlproc.py
<?xml version='1.0' encoding='UTF-8'?>
<objects>
  <g id="hello" inkscape:label="hello">
    <circle id="there" inkscape:label="there"/>
    <rect id="world" inkscape:label="world"/>
  </g>
  <g id="layer4">
    <circle id="more" inkscape:label="more"/>
  </g>
</objects>
... which is exactly what I wanted.
So to specify in the spirit of how the question is postulated - how do I achieve this with xmlstarlet?
 
     
    