使用 Bash 将 XML 数据拆分为变量

使用 Bash 将 XML 数据拆分为变量

我正在尝试从服务下载一些文件。这些文件位于 XML 文件中。 XML 文件可以有一个或多个要下载的文件。但是,现在我的脚本有问题。我不知道如何将 XMLLINT 中的字符串拆分为数组,以便我可以单独下载每个文件。

我需要将字符串拆分为多个变量,然后下载 URL 字符串的每个文件。

然而,文件 201701_1 不会重复,因此,我使用curl 下载它们没有任何问题。但文件coverage.zip 重复并且它们被curl 覆盖。我做:然后我做curl来下载单个文件。

curl -O -b cookie $URL 

目前,我的脚本如下:

while read edition; do   XML="<?xml version=\"1.0\"
encoding=\"UTF-8\"?> <download-area>   <files>
    <file>
      <url>https://google.com/411/201701_01_01.zip</url>
    </file>
    <file>
      <url>https://google.com/411/201701_01_02.zip</url>
    </file>   </files> </download-area>
    "
    URL=$(echo $XML | xmllint --xpath \
    "/*[name()='download-area']/*[name()='files']/*[name()='file']/*[name()='url']/text()" -)

    echo "URL:: " $URL

done < $LATEST_EDITION

LATEST_EDITION 只是一个包含行的文件。

我的问题是::如何将 VAR_1 和 VAR_2 拆分为多个 URL,以便我可以单独下载它们?如何防止coverage.zip被覆盖?

答案1

xmllint从 XML 文档中提取信息毫无用处。您可能需要考虑xmlstarletor xml_grep(来自perl的 XML::Twig)或xml2

使用xmllint,您仍然可以一次提取一个字符串:

VAR1=$(printf '%s\n' "$XML" |
  xmllint --xpath '/download-area/files/file[1]/url/text()' -)
VAR2=$(printf '%s\n' "$XML" |
  xmllint --xpath '/download-area/files/file[2]/url/text()' -)

对于像这里这样不包含换行符的值,您可以使用bash'sreadarray作为:

readarray -t var < <(
  xmlstarlet sel -t -v /download-area/files/file/url  <<< "$XML")

或者

readarray -t var < <(
  xml2 <<< "$XML" | sed -n 's|^/download-area/files/file/url=||p')

或者:

readarray -t var < <(
  xml_grep --text_only /download-area/files/file/url <<< "$URL")

答案2

尝试类似的方法:

declate -a url_array
url_array=(`echo $XML | grep -o "http.*zip" | tr '\n' ' '`)

答案3

xmllint不是一个用于分割 XML 的好工具。为了解决你的两个问题(解析 XML,并确保唯一的 URL,我认为?)以一种稳健的方式使用bashxmlstarlet

#!/bin/bash
XML='<?xml version="1.0" encoding="UTF-8"?>
<download-area>
  <files>
    <file>
      <url>https://google.com/411/201701_01_01.zip</url>
    </file>
    <file>
      <url>https://google.com/411/201701_01_02.zip</url>
    </file>
  </files>
</download-area>'

# IFS=$'\n'   ## required if URLs contains spaces
urls=( $(xml select -t -m  "/download-area/files/file" -v url -nl  <<< $XML ) )

declare -A unique  # associative array
for uu in ${urls[*]}; do let unique[$uu]++; done

for uu in "${!unique[@]}"; do
  printf "URL is %s\n" ${uu}
done

xmlstarletselect模式中使用,模板 ( -t) 与 xpath 匹配 ( ),从中选择节点的-m值 ( ) 并在每个值后面添加换行符 ( )。 (xmlstarlet 比这更灵活,您可以多次使用并在需要的地方添加任意文本。)-vurl-nl-v-o

这也使用了<<<保存echo/pipe 的重定向。

URL 存储在普通索引数组中urls。接下来,循环数组以将 URL 作为键存储在关联数组— 这解决了唯一性问题(并且出现次数保留为每个条目的值)。

如果您不熟悉 bash 的关联数组,第二个循环需要一些额外的解释。该表达式"${unique[@]}"扩展了所有价值观数组的,这使用"${!unique[@]}"它来扩展所有索引对于数组,如果您使用以下命令转储数组数据,这应该有意义declare -p unique

declare -A unique=([https://google.com/411/201701_01_01.zip]="1" 
                   [https://google.com/411/201701_01_02.zip]="1" )

您甚至可以在一个循环中完成所有这一切,尽管这可能有点难以理解:

while read line; do
  [[ -n "$line" ]] && let unique[$line]++ 
done < <(xml sel -t -m  "/download-area/files/file" -v url  -nl <<< $XML)

XMLstarlet 可以安装为xml或者xmlstarlet

答案4

考虑使用sed来解析 的输出xmllint。请注意缩短的 XPath 表达式!

URL=$( echo $XML | xmllint --xpath "//url" - | sed -e 's/<url>//g' -e 's/<\/url>/\n/g' )

printf "%s\n" "$URL"

这将每行输出一个 URL

相关内容