Search code examples
xmlshellxpathxmlstarlet

xmlstartlet shell script to add attribute and elements under an attribute


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.


Solution

  • 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>