Excluir a última linha do arquivo gz

1

Eu preciso excluir a última linha do arquivo gz sem descompactar. O arquivo tem 500 linhas.

Como posso fazer isso?

Eu tentei:

 gzip -dc "$files" | tail -500 | gzip -c > "$files".tmp

Mas isso não funciona.

    
por ANOUK_prog 08.07.2016 / 19:04

4 respostas

4

Você não pode modificar um arquivo compactado sem descompactá-lo.

No mínimo, para deletar todo o texto após a 499ª linha, você tem que descompactar as primeiras 499 linhas para encontrar onde a 499ª linha termina. Se você quiser excluir a última linha, independentemente de quantas linhas houver, será necessário descompactar o arquivo inteiro para identificar onde a última linha começa.

Não há atalho porque o arquivo está compactado. A codificação de um caractere depende de todos os caracteres anteriores - o princípio básico da compactação gzip é usar sequências de bits mais curtas para sequências de caracteres encontradas anteriormente e sequências de bits ligeiramente maiores para sequências de caracteres ainda não encontradas. produzindo um arquivo menor quando as seqüências de caracteres são repetidas. Não há como determinar se um determinado caractere é uma quebra de linha sem examinar todos os caracteres anteriores.

Sua tentativa, que descompacta o arquivo, funciona no fluxo descompactado e recompõe para outro arquivo, está no caminho certo. Você só precisa do comando correto para truncar o arquivo: tail -500 mantém as últimas 500 linhas, o que não é o que você deseja. Use head -n 499 para manter as primeiras 499 linhas ou head -n -1 para remover a última linha. Nem todos os sistemas suportam um argumento negativo para head ; se o seu não, você pode usar sed '$d' .

gunzip <"$file" | head -n -1 | gzip >"$file".tmp
mv -- "$file".tmp "$file"

Note que você não pode gravar diretamente no arquivo: gunzip <"$file" | … | gzip >"$file" iria começar a sobrescrever o arquivo enquanto gunzip ainda o está lendo. Os comandos em um pipeline são executados em paralelo. Embora seja possível evitar a criação de um arquivo temporário, é uma má ideia, porque qualquer maneira de fazer isso resultaria em um arquivo truncado se o comando fosse interrompido, então não discutirei como fazer isso.

Em teoria, seria possível truncar um arquivo gzipado por:

  1. descompactando na memória para determinar a posição em que você deseja truncá-lo;
  2. truncando o arquivo para remover todos os dados após o último caractere a ser mantido;
  3. sobrescreve os últimos bytes para codificar corretamente o último caractere;
  4. substitua alguns bytes no início para refletir o novo tamanho de arquivo.

No entanto, isso não pode ser feito com ferramentas padrão, precisaria de alguma programação personalizada e deixaria um arquivo inválido se fosse interrompido.

    
por 09.07.2016 / 03:03
5

Assumindo, a partir do seu exemplo, que a descompactação para um fluxo é aceitável, mas você deseja evitar a descompactação para um arquivo. Você deveria ser capaz de

gzip -cd "$files" | sed -e '$d' | gzip > "$files".tmp

usando sed para ir até a última linha e excluí-la.

    
por 08.07.2016 / 19:24
2

Você pode usar zcat .

zcat <file> | head -n <lines>

Somente descompacta o suficiente para transmitir essas n linhas.

Outras leituras: link

    
por 25.09.2017 / 04:24
1

Baseando-se na resposta de @Eric Renouf, (desculpe, isso é muito longo para um comentário), para manter os metadados originais de timestamp e nome de arquivo no arquivo, envolva-o com:

gzip -cd "$file" | sed -e '$d' > "$file.tmp"
touch -r "$file" "$file.tmp"
# optionally keep the old file
# mv "$files" "$file.old"
mv "$file.tmp" "$file"
gzip "$file"

Ou, como há um arquivo descompactado aqui, use xz em vez de gzip para recomprimi-lo. Melhor compactação e geralmente mais rápida.

    
por 09.07.2016 / 16:43