use xmlstarlet in shell script to read script variable value and insert in outpu xml file under specific section if the string is not present there

55 views Asked by At

The parse yaml utility which I used in my shell script produces variables as below by eval command

policy1_name='ipfilter'
policy1_scope='api'
policy1_apiname='apiname'
policy1_inboundsession='rate-limit-by-key calls="25" renewal-period="60"
counter-key="@\(Regex.Match\(context.xxx.xxxx.GetValueOrDefault\("X-xxxxxxx-For",""\), @"^[.0-9]*"\)?.Value\)'
policy1_outboundsession_ipAddressesFrom='1xxxxxxxx'
policy1_outboundsession_ipAddressesTo='1yyyyyyy'
policy1_=' policy1_name policy1_scope policy1_apiname policy1_inboundsession policy1_outboundsession'
policy1_outboundsession_=' policy1_outboundsession_ipAddressesFrom policy1_outboundsession_ipAddressesTo'
policy_=' policy1'

I know, I can use xmlstarlet to create element inside my outputpolicy.xml file as given below.

for g in $(eval echo \$${f}_apiname); do
     echo " this policy will be applied to apis,$(eval echo \$${f}_apiname)"
    done
      if [ -z "$(eval echo \$${f}_inboundsession_)" ]; then
        echo 'the inbound session is not present'
      else
        echo 'the inbound session is present and append the policy settings to inbound'
        xmlstarlet ed -O  -s '//inbound' -t elem -n rate-limit-by-key  -i '//inbound/rate-limit-by-key' -t attr -n calls -v xx -i '//inbound/rate-limit-by-key' -t attr -n renewal-period -v yy -i '//inbound/rate-limit-by-key' -t attr -n counter-key -v '@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)' policy.xml > io_ou_ipfilter.xml

but what I am looking is use xmlstarlet in my script to read the output of the above script variable "$(eval echo $${f}inboundsession) and insert the given string under inbound session of the below policy.xml file if the string is not preset.

Expected Output

<policies>
    <inbound>
      <base />
      <rate-limit-by-key calls="xxx" renewal-period="xx" counter-key="@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)" />
    </inbound>
    <backend>
      <base />
    </backend>
    <outbound>
      <base />
    </outbound>
    <on-error>
      <base />
    </on-error>
</policies>
1

There are 1 answers

0
urznow On

Here's how you could add the rate-limit-by-key node, or not if it exists.

# shellcheck shell=sh disable=SC2016

xmlstarlet edit -O \
  -a '/policies/inbound/base[not(following-sibling::rate-limit-by-key)]' \
    -t elem -n 'rate-limit-by-key' \
  --var lim '$prev' \
  -s '$lim' -t attr -n 'calls' -v 'xx' \
  -s '$lim' -t attr -n 'renewal-period' -v 'yy' \
  -s '$lim' -t attr -n 'counter-key' \
    -v '@(Regex.Match(context.x.y.GetValueOrDefault("xxxxxxx",""), @"^[.x-y]*")?.Value)' \
"${infile:-file.xml}"

where:

  • -a … (aka --append) adds a following-sibling node, it executes only if its argument matches an existing node and so the XPath predicate (not(following-sibling::rate-limit-by-key)) specifies an add-if-not-exists condition
  • if -a … matches nothing then $prev [] will match nothing and this will nullify the following $prev-dependent -s … (aka --subnode) options
  • the -v (aka --value) options here use string literals but they can be changed to pull in a shell variable (-v "${somevar}") or the result of a command substitution ( -v "$(somecmd)")

[] In 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).


Note that the expected output you posted is not XML as it contains quote characters in an attribute value which an XML serializer will likely output as &quot;.

Given this input file,

<policies>
  <inbound>
    <base/>
  </inbound>
</policies>

the command above produces,

<policies>
  <inbound>
    <base/>
    <rate-limit-by-key calls="xx" renewal-period="yy" counter-key="@(Regex.Match(context.x.y.GetValueOrDefault(&quot;xxxxxxx&quot;,&quot;&quot;), @&quot;^[.x-y]*&quot;)?.Value)"/>
  </inbound>
</policies>