Limpar arquivos XML concatenados

2

Estou tentando corrigir meu Script de exportação delicioso para trabalhar com o novo "< href="http://code.davidjanes.com/blog/2011/10/29/good-bye-delicious/"> truque "de servir apenas 1000 marcadores de cada vez. O script concatena resultados (XML) de várias chamadas em um único arquivo, e agora eu tenho que me livrar do texto do cabeçalho e rodapé do arquivo resultante. Exemplo:

<?xml ...
<posts ...
  <post ...
  ...          # 998 other posts
  <post ...
</posts>     # Line 1003
# The above lines are repeated N times before the final line:
</posts>

Em outras palavras, eu quero remover todas as linhas que não começam com <post entre (excluindo) a terceira e a última linha, e cada bloco de XML é 1003 linhas exatamente, exceto possivelmente a última.

Suponho que sed ou awk seja perfeito para isso.

    
por l0b0 24.11.2011 / 23:09

3 respostas

1

Entendi:

sed -i -e '3,${/^</d}' file

Em outras palavras, entre as linhas 3 e a última linha, remova qualquer linha que comece com < . Desculpe o recuo não apareceu no post original.

    
por 25.11.2011 / 00:18
1

Por causa do formato consistente dos dados, cabeça e cauda são seus amigos. Isso deve funcionar para o último arquivo mais curto.

cat file | tail -n +3 | head -n -1 > trimmed_file

O tail -n +3 leva tudo da 3ª linha até o final do arquivo, e o head -n -1 leva tudo, exceto a última linha do arquivo.

Depois de ter um conjunto de arquivos aparados, inclua-os em conjunto com uma seção de cabeçalho e rodapé apropriada para o arquivo inteiro.

UPDATE: para evitar a criação de muitos arquivos extras, basta envolvê-lo em um loop:

for i in *
do
    cat $i | tail -n +3 | head -n -1 >> newfile
done

Obtenha um modelo para o cabeçalho executando um dos arquivos por meio do cabeçalho para extrair as primeiras 3 linhas e, em seguida, execute o loop for acima. Em seguida, faça uma coisa semelhante com tail para obter a última linha de um dos arquivos e anexe-o ao newfile. Eu imagino que você precisará atualizar as informações de cabeçalho e rodapé.

    
por 24.11.2011 / 23:24
0

Isso parece um pouco desajeitado. Por que não processar os dados conforme eles chegam?

bookmarks_count=$chunk_size
total_bookmarks_count=0
{
  while [ $bookmarks_count -eq $chunk_size ]; do
    chunk=$(wget … -O - "$EXPORT_URL?start=$total_bookmarks_count")
    bookmarks_count=$(printf %s "$chunk" | grep -c "$bookmark_prefix")
    total_bookmarks_count=$((total_bookmarks_count + bookmarks_count))
    printf %s "$chunk" |
    sed -e 's#><#>\n<#g' -e "$EXPORT_COMPATIBILITY" -e "$EXPORT_COMPATIBILITY"
  done
  echo '<\/posts>'
} >"$EXPORT_PATH"

Você pode até evitar armazenar cada parte da memória, embora seja um pouco mais complicado. Aqui está um método que só funciona em ksh e zsh; em outros shells, o lado direito do pipeline é executado em um subshell, portanto o valor de total_bookmarks_count não é atualizado.

{
  total_bookmarks_count=0
  while
      wget … -O - "$EXPORT_URL?start=$bookmarks_count" |
      sed -e … |
      tee /dev/fd/3 |
      this_chunk_size=$(grep -c "$bookmark_prefix")
      [[ $this_chunk_size = $chunk_size ]]
  do
    ((total_bookmarks_count += chunk_size))
  done
  echo '<\/posts>' >&3
} 3>"$EXPORT_PATH"

Aqui está uma maneira de fazer esse método funcionar em outros shells, onde a única informação que você pode obter de um pipeline é seu status de retorno.

: >"$EXPORT_PATH"
total_bookmarks_count=0
while
    wget … -O - "$EXPORT_URL?start=$bookmarks_count" |
    sed -e … |
    tee -a "$EXPORT_PATH" |
    [ $(grep -c "$bookmark_prefix") = $chunk_size ]
do
  total_bookmarks_count=$((total_bookmarks_count + chunk_size))
done
echo '<\/posts>' >> "$EXPORT_PATH"
    
por 25.11.2011 / 02:31

Tags