![使用 Bash 将 XML 数据拆分为变量](https://linux22.com/image/122047/%E4%BD%BF%E7%94%A8%20Bash%20%E5%B0%86%20XML%20%E6%95%B0%E6%8D%AE%E6%8B%86%E5%88%86%E4%B8%BA%E5%8F%98%E9%87%8F.png)
我正在尝试从服务下载一些文件。这些文件位于 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 文档中提取信息毫无用处。您可能需要考虑xmlstarlet
or 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,我认为?)以一种稳健的方式使用bash
和xmlstarlet
:
#!/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
这xmlstarlet
在select
模式中使用,模板 ( -t
) 与 xpath 匹配 ( ),从中选择节点的-m
值 ( ) 并在每个值后面添加换行符 ( )。 (xmlstarlet 比这更灵活,您可以多次使用并在需要的地方添加任意文本。)-v
url
-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