Usando o Bash para dividir dados XML em variáveis

2

Estou tentando baixar alguns arquivos de um serviço. Os arquivos são encontrados em um arquivo XML. O arquivo XML pode ter um único arquivo ou vários arquivos para download. No entanto, agora eu tenho um problema com o meu script. Eu não sei como dividir a string de XMLLINT em array para que eu possa fazer o download de cada arquivo individualmente.

Eu preciso dividir a string em várias variáveis e, em seguida, fazer o download de cada arquivo da string de URL.

No entanto, o arquivo 201701_1 não se repete e, portanto, eu os baixo usando o curl sem problemas. Mas os arquivos coverage.zip se repetem e são sobrescritos por curl. Eu faço: Então eu faço o curl para baixar arquivos individuais.

curl -O -b cookie $URL 

No momento, meu script é o seguinte:

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 é simplesmente um arquivo com linhas.

Minha pergunta é: Como posso dividir VAR_1 e VAR_2 em várias URLs para que eu possa baixá-las individualmente? Como posso evitar que coverage.zip seja sobrescrito?

    
por Noel Alex Makumuli 20.11.2017 / 08:22

4 respostas

1

Tente algo como:

declate -a url_array
url_array=('echo $XML | grep -o "http.*zip" | tr '\n' ' '')
    
por 20.11.2017 / 09:50
1

xmllint não é uma ótima ferramenta para cortar e separar XML. Para resolver seus dois problemas (analisando XML e garantindo URLs exclusivas, eu acho?) De forma robusta usando bash e 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

Isso usa o modo xmlstarlet in select , com um modelo ( -t ) que corresponde ( -m ) a um caminho x a partir do qual seleciona o valor ( -v ) do nó url e adiciona uma nova linha ( -nl ) depois de cada uma. (xmlstarlet é ainda mais flexível do que isso, você pode usar -v várias vezes e -o para adicionar texto arbitrário sempre que necessário.)

Isso também usa o redirecionamento <<< , que salva echo / pipe.

As URLs são armazenadas em uma matriz indexada normal urls . Em seguida, faça um loop sobre a matriz para armazenar as URLs como as chaves em uma matriz associativa - isso resolve o problema de unicidade (e a contagem de ocorrências é mantida como o valor de cada entrada).

Se você não estiver familiarizado com os arrays associativos de bash, o segundo loop precisa de alguma explicação adicional. A expressão "${unique[@]}" expande todos os valores de uma matriz, isso usa "${!unique[@]}" , que em vez expande todos os índices de uma matriz, isso deve fazer sentido se você esvaziar dados da matriz com declare -p unique :

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

Você pode até fazer tudo isso em um único loop, embora seja um pouco difícil de entender, talvez:

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

O XMLstarlet pode ser instalado como xml ou xmlstarlet

    
por 20.11.2017 / 12:20
1

xmllint é bastante inútil para extrair informações de documentos XML. Você pode considerar xmlstarlet ou xml_grep (da perl 's XML :: Twig) ou xml2 .

Com xmllint , você ainda pode extrair uma string de cada vez com:

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()' -)

Para valores como os que não contêm caracteres de nova linha, você pode usar bash ' readarray as:

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

Ou

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

Ou:

readarray -t var < <(
  xml_grep --text_only /download-area/files/file/url <<< "$URL")
    
por 20.11.2017 / 13:10
0

Considere usar sed para analisar a saída de xmllint . Observe a expressão XPath encurtada!

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

printf "%s\n" "$URL"

Isso produzirá os URLs um por linha

    
por 21.11.2017 / 02:48