Чтение ключей из файла XML в массив оболочки

Я пытаюсь проанализировать XML-файл и получить все значения тегов и шрифтов и добавить их в ассоциативные массивы. Моя проблема в том, что массив, кажется, не имеет значения, назначенные ему правильно

#!/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]}

Последнее эхо просто повторяет конец тега > Немного запутался в том, как массивы должны работать в оболочке или есть ли лучший способ приблизиться к этому.

Мой XML выглядит следующим образом.

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

Я пытаюсь создать решение, которое работает с GNU bash, версия 3.2.57(1)-релиз (x86_64-apple-darwin16)

1 ответ

Решение

Если у вас bash 4.0 или новее:

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

Иначе:

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

В любом из них вывод xmlstarlet это просто ключи, которые вы пытаетесь извлечь, как в:

betProper
linesProper
spinsProper
bet
line

... и это можно повторить, как и следовало ожидать:

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

Если у вас нет xmlstarletвы можете запустить XSLT, эквивалентный приведенной выше командной строке; если у вас есть таблица стилей print-strings.xslt со следующим содержанием:

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

... тогда вы можете запустить:

xsltproc print-strings.xslt in.xml

... чтобы получить тот же результат, что и xmlstarlet sel -t -m '//strings/string[@key]' -v @key -n <in.xml,

Другие вопросы по тегам