-1

I am trying to add attribute under an existing attribute in an XML file. I have been using xmlstarted for edit but never used it for update. Would be helpful is someone tell how to add a new attr and an element under that new attrb -

Here is the snippet of my xml file -

Here is what i usually does to edit the sub elements with the hel;p of xmlstarlet-

xmlstarlet edit  --inplace -u "/allocations/queue[@name='root']/queue[@name='paas_api_q1']/queue[@name='child1_sq1']/minResources" --value "$value" myfile.xml

Before -

<?xml version="1.0"?>
<allocations>
  <queue name="root">
    <aclSubmitApps> bddbagrp,mapr</aclSubmitApps>
    <aclAdministerApps> bddbagrp,root,mapr,trmte_id</aclAdministerApps>
    <schedulingPolicy>drf</schedulingPolicy>
    <defaultMinSharePreemptionTimeout>60</defaultMinSharePreemptionTimeout>
    <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
    <queue name="paas_api_q1">
      <minResources>90000 mb,15 vcores,2 disks</minResources>
      <maxResources>135000 mb,22 vcores,3 disks</maxResources>
      <queue name="child1_sq1">
        <minResources>66000 mb,11 vcores,2 disks</minResources>
        <maxResources>264000 mb,44 vcores,8 disks</maxResources>
        <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
        <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
        <label>allnodes||balanced</label>
      </queue>
    </queue>
  </queue>
  <queuePlacementPolicy>
    <rule create="false" name="specified"/>
    <rule name="reject"/>
  </queuePlacementPolicy>
</allocations>

After it should look like this.. Here i am adding a new attribute under an existing attr - pass_api_q1 .

<!-- language: lang-xml -->
<?xml version="1.0"?>
<allocations>
  <queue name="root">
    <aclSubmitApps> bddbagrp,mapr</aclSubmitApps>
    <aclAdministerApps> bddbagrp,root,mapr,trmte_id</aclAdministerApps>
    <schedulingPolicy>drf</schedulingPolicy>
    <defaultMinSharePreemptionTimeout>60</defaultMinSharePreemptionTimeout>
    <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
    <queue name="paas_api_q1">
      <minResources>90000 mb,15 vcores,2 disks</minResources>
      <maxResources>135000 mb,22 vcores,3 disks</maxResources>
      <queue name="child1_sq1">
        <minResources>66000 mb,11 vcores,2 disks</minResources>
        <maxResources>264000 mb,44 vcores,8 disks</maxResources>
        <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
        <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
        <label>allnodes||balanced</label>
      </queue>
      <queue name="child2_sq1">
        <minResources>66000 mb,11 vcores,2 disks</minResources>
        <maxResources>132000 mb,22 vcores,4 disks</maxResources>
        <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
        <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
        <label>allnodes||balanced</label>
      </queue>
    </queue>
  </queue>
  <queuePlacementPolicy>
    <rule create="false" name="specified"/>
    <rule name="reject"/>
  </queuePlacementPolicy>
</allocations>

Any help/direction would be appreciated.

3
  • Add your own code to your question. Commented May 22, 2020 at 11:34
  • Looks like you added a new <queue> node that is the same as the one preceding it (including the children) except that it has a name attribute with a "child2_sq1" value, while the previous node's attribute value is "child1_sq1". Is that the intent? Commented May 22, 2020 at 17:24
  • yes.. That's correct Commented May 26, 2020 at 8:07

2 Answers 2

2

You could use a bunch of -i/-a/-s to add the new elements (see here), but I think it would be easier to use XSLT with tr. You could put your elements in another file and pass it in as a parameter.

Example...

XML fragment to add (fragment.xml)

<?xml version="1.0"?>
<queue name="child2_sq1">
    <minResources>66000 mb,11 vcores,2 disks</minResources>
    <maxResources>132000 mb,22 vcores,4 disks</maxResources>
    <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
    <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
    <label>allnodes||balanced</label>
</queue>

XSLT (test.xsl)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="frag"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!--If you wanted to make this stylesheet reusable, you could pass a new
  value in for the "match" attribute by selecting this template by its
  "name" value (@name='target').-->
  <xsl:template match="queue[@name='paas_api_q1']" name="target">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:copy-of select="$frag"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

xmlstarlet command line

xmlstarlet tr test.xsl -p frag='document("fragment.xml")/*' input.xml

Output

<?xml version="1.0"?>
<allocations>
  <queue name="root">
    <aclSubmitApps> bddbagrp,mapr</aclSubmitApps>
    <aclAdministerApps> bddbagrp,root,mapr,trmte_id</aclAdministerApps>
    <schedulingPolicy>drf</schedulingPolicy>
    <defaultMinSharePreemptionTimeout>60</defaultMinSharePreemptionTimeout>
    <fairSharePreemptionTimeout>60</fairSharePreemptionTimeout>
    <queue name="paas_api_q1">
      <minResources>90000 mb,15 vcores,2 disks</minResources>
      <maxResources>135000 mb,22 vcores,3 disks</maxResources>
      <queue name="child1_sq1">
        <minResources>66000 mb,11 vcores,2 disks</minResources>
        <maxResources>264000 mb,44 vcores,8 disks</maxResources>
        <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
        <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
        <label>allnodes||balanced</label>
      </queue>
      <queue name="child2_sq1">
        <minResources>66000 mb,11 vcores,2 disks</minResources>
        <maxResources>132000 mb,22 vcores,4 disks</maxResources>
        <aclSubmitApps> paas_opsauto_admin_unix</aclSubmitApps>
        <aclAdministerApps> paas_opsauto_admin_unix</aclAdministerApps>
        <label>allnodes||balanced</label>
      </queue>
    </queue>
  </queue>
  <queuePlacementPolicy>
    <rule create="false" name="specified"/>
    <rule name="reject"/>
  </queuePlacementPolicy>
</allocations>
Sign up to request clarification or add additional context in comments.

Comments

0

As Daniel Haley points out, using xmlstarlet exclusively can be tedious because it doesn't have a copy/paste functionality which would have made the task much simpler. As an exercise, I tried the code below which seems to work:

xmlstarlet ed  
     -a "//queue/queue[@name='paas_api_q1']/queue"
     -t elem -n queue -v "$(xmlstarlet sel  -t -m //queue/queue[@name]/*/* -c . myfile.xml)"
     -i "//queue[@name='paas_api_q1']//queue[not(@name)]" --type attr --name "name" -v "child2_sq1" \
   myfile.xml | xmlstarlet unesc

Basically, it creates a new node in the appropriate place, creates a copy of the original node (using a variable), inserts the copy into the value of new node, adding an attribute and a value to the new node and, finally, unescaping the tags from the copy of the original node.

As I said, just an exercise...

While I haven't tried it, I would guess that this task could also be performed using, instead of xmlstarlet, xidel and its support for xquery.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.