0

Im trying to parse an xml file and get all tag values and fonts values and add them to an associative arrays. My issue is that the array doesn't seem to be having the values assigned to it properly

#!/bin/bash

GAME_NAME="."
LOCALIZATION_DIR="$GAME_NAME/assets/data/localization"
INDEX=0
OUTPUT_KEYS=()
# parse english xml for tags and font names first
for str in $(echo "cat //strings/string/@key" | xmllint --shell "$LOCALIZATION_DIR/en.xml")
do
    echo "$str"
    echo "--"
    OUTPUT_KEYS[$index]="$str"
    ((INDEX++))
done
echo ${OUTPUT_KEYS[0]}

The last echo just echos the end of the tag > A little confused on how arrays should be working in shell or if there is a better way to approach this.

My XML looks like this.

<?xml version="1.0" encoding="UTF-8" ?>
<strings version="5.6051.4-en">
    <!--<StarLineUI>-->
    <!-- Menu -->
    <string key="betProper"             value="Bet"                 fonts="uiAccountTitle" />
    <string key="linesProper"           value="Lines"               fonts="uiAccountTitle" />
    <string key="spinsProper"           value="Spins"               fonts="uiAccountTitle" />

    <string key="bet"                   value="BET"                 fonts="uiMenuTitle, uiAccountTitle" />
    <string key="line"                  value="LINE"                fonts="uiMessage" />
</strings>

I'm trying to build a solution that works with GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)

6
  • Don't read lines with for. See BashFAQ #1 for best-practice on reading line-oriented input. Commented Jul 20, 2017 at 16:39
  • Also, where are you incrementing your index? (And you're using index in one place and INDEX in the other; best-practice is lowercase; see fourth paragraph of pubs.opengroup.org/onlinepubs/9699919799/basedefs/… for naming conventions). Commented Jul 20, 2017 at 16:39
  • Also, are you really sold on xmllint as a tool? If you have XMLStarlet, I could offhand provide something that'll be much easier to parse than output of xmllint --shell. Commented Jul 20, 2017 at 16:41
  • Also, which version of bash? If you have 4.0 or newer, readarray/mapfile is the more sensible way to populate your array w/ values here. Commented Jul 20, 2017 at 16:42
  • Sorry I missed the index. Its incrementing I removed a lot of other code to help narrow down the issue for you guys so your not reading it all. Updated for index Commented Jul 20, 2017 at 16:53

1 Answer 1

1

If you have bash 4.0 or newer:

readarray -t output_keys \
  < <(xmlstarlet sel -t -m '//strings/string[@key]' -v @key -n <in.xml)
echo "${output_keys[0]}"

Otherwise:

output_keys=( )
while IFS= read -r line; do
  output_keys+=( "$line" )
done < <(xmlstarlet sel -t -m '//strings/string[@key]' -v @key -n <in.xml)

In either of these, the output of xmlstarlet is just the keys you're trying to extract, as in:

betProper
linesProper
spinsProper
bet
line

...and this can be iterated over as you'd expect:

for key in "${output_keys[@]}"; do
  echo "Found key: $key"
done

If you don't have xmlstarlet, you can run the XSLT equivalent to the above command line; if you have a stylesheet print-strings.xslt with the following contents:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
  <xsl:output omit-xml-declaration="yes" indent="no"/>
  <xsl:template match="/">
    <xsl:for-each select="//strings/string[@key]">
      <xsl:call-template name="value-of-template">
        <xsl:with-param name="select" select="@key"/>
      </xsl:call-template>
      <xsl:value-of select="'&#10;'"/>
    </xsl:for-each>
  </xsl:template>
  <xsl:template name="value-of-template">
    <xsl:param name="select"/>
    <xsl:value-of select="$select"/>
    <xsl:for-each select="exslt:node-set($select)[position()&gt;1]">
      <xsl:value-of select="'&#10;'"/>
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

...then you can run:

xsltproc print-strings.xslt in.xml

...to get the same output as xmlstarlet sel -t -m '//strings/string[@key]' -v @key -n <in.xml.

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks will try out xmlstarlet
Yes, it does; it's available on both MacPorts and Homebrew.
(and you can install a modern bash the same way; the 3.2.x build Apple ships is just barely short of a decade old -- they intentionally use an ancient one so they don't need to comply with the GPLv3 license terms modern versions of bash are developed under).
Seems the most clean and easy to read way would be to use xmlstarlet but as other people are running this script ( artists) I want to make it as accessible as possible for them without them needing to update a bunch of things. The xsltproc command works perfectly for this, thanks!
How would I handle fonts="uiMenuTitle, uiAccountTitle" /> it seems to be only giving me the first value and then the second one the next time.
|

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.