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.
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 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é.
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"