bash+xmlstarlet:如何索引到列表或填充数组?

sco*_*lli 2 bash xmlstarlet

我尝试使用 xmlstarlet 从以下示例 XML 中选择单个节点:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="key.xsl" ?>
<tables>
  <tableset>
    <table name="table1">
      <row>
        <fld name="fileName">
          <strval><![CDATA[/my/XYZ/file1]]></strval>
        </fld>
        <fld name="fileName">
          <strval><![CDATA[/my/XYZ/file2]]></strval>
        </fld>
        <fld name="fileName">
          <strval><![CDATA[/my/other/XYZ/file3]]></strval>
        </fld>
        <fld name="worksBecauseUnique">
          <strval><![CDATA[/XYZ/unique]]></strval>
        </fld>
      </row>
    </table>
  </tableset>
</tables>
Run Code Online (Sandbox Code Playgroud)

我正在尝试在 bash 中构建关联数组...如何选择单个节点,或使用 xmlstarlet 迭代多个节点?

到目前为止,我正在尝试类似以下的方法,但它不起作用:

xmlstarlet sel -t -v "//tables/tableset/table/row/fld[@name=\"fileName\"]/strval[0]" xmlfile.xml
Run Code Online (Sandbox Code Playgroud)

希望得到“/my/XYZ/file1”,但这不起作用。

Cha*_*ffy 6

回答你问题的第一部分,你犯了一个简单的错误:

strval[0]
Run Code Online (Sandbox Code Playgroud)

需要是

strval[1]
Run Code Online (Sandbox Code Playgroud)

...选择第一个实例,因为 XPath 数组是 1 索引的,而不是 0 索引的。


现在,当您想要在整个文档中而不是在父文档中选择第二个匹配项时fld,看起来有点不同:

(//tables/tableset/table/row/fld[@name="fileName"]/strval)[2]
Run Code Online (Sandbox Code Playgroud)

现在开始填充 shell 数组。由于您的内容不包含换行符:

query='//tables/tableset/table/row/fld[@name="fileName"]/strval'

fileNames=( )
while IFS= read -r entry; do
  fileNames+=( "$entry" )
done < <(xmlstarlet sel -t -v "$query" -n xmlfile.xml)

# print results
printf 'Extracted filename: %q\n' "${fileNames[@]}"
Run Code Online (Sandbox Code Playgroud)

您没有提供足够的详细信息来设置关联数组(您想如何建立键?),因此我将其作为一个简单的索引数组来执行。


另一方面,如果我们要做一些假设——您想要设置关联数组以匹配键@namestrval值,并且在为同一键给出多个值时想要使用换行符分隔多个值—— - 那么可能看起来像这样:

match='//tables/tableset/table/row/fld[@name][strval]'
key_query='./@name'
value_query='./strval'

declare -A content=( )
while IFS= read -r key && IFS= read -r value; do
  if [[ $content[$key] ]]; then
    # appending to existing value
    content[$key]+=$'\n'"$value"
  else
    # first value for this key
    content[$key]="$value"
  fi
  fileNames+=( "$entry" )
done < <(xmlstarlet sel \
           -t -m "$query" \
           -v "$key_query" -n \
           -v "$value_query" -n xmlfile.xml)
Run Code Online (Sandbox Code Playgroud)