Como truncar arquivo por linhas?

13

Eu tenho um grande número de arquivos, alguns dos quais são muito longos. Eu gostaria de truncá-los para um determinado tamanho, se eles são maiores, removendo o final do arquivo. Mas eu só quero remover linhas inteiras. Como posso fazer isso? Parece o tipo de coisa que seria manipulada pela cadeia de ferramentas do Linux, mas eu não sei o comando certo.

Por exemplo, digamos que eu tenha um arquivo de 120.000 bytes com linhas de 300 bytes e estou tentando truncá-lo para 10.000 bytes. As primeiras 33 linhas devem permanecer (9900 bytes) e o restante deve ser cortado. Eu não quero cortar exatamente 10.000 bytes, pois isso deixaria uma linha parcial.

É claro que os arquivos são de diferentes comprimentos e as linhas não são todas do mesmo tamanho.

O ideal seria que os arquivos resultantes ficassem um pouco mais curtos do que um pouco mais longos (se o ponto de interrupção estivesse em uma linha longa), mas isso não é muito importante, poderia ser um pouco mais longo se fosse mais fácil. Eu gostaria que as alterações fossem feitas diretamente nos arquivos (bem, possivelmente o novo arquivo copiado em outro lugar, o original excluído e o novo arquivo movido, mas isso é o mesmo do POV do usuário). Uma solução que redireciona os dados para vários lugares e, em seguida, retorna a possibilidade de corromper o arquivo e eu gostaria de evitar isso ...

    
por Charles 24.07.2012 / 09:00

5 respostas

1

A sed / wc complexidade pode ser evitada nas respostas anteriores se awk for usado. Usando o exemplo fornecido pelo OP (mostrando linhas completas antes de 10000 bytes):

awk '{i += (length() + 1); if (i <= 10000) print $ALL}' myfile.txt

Também mostrando a linha completa contendo 10000 bytes, se esse byte não estiver no final da linha:

awk '{i += (length() + 1); print $ALL; if (i >= 10000) exit}' myfile.txt

A resposta acima assume:

  1. O arquivo de texto é do terminador de linha Unix ( \n ). Para arquivos de texto do DOS / Windows ( \r\n ), altere length() + 1 para length() + 2
  2. O arquivo de texto contém apenas um caractere de byte. Se houver um caractere multibyte (como no ambiente unicode), defina o ambiente LC_CTYPE=C para forçar a interpretação no nível de byte.
por 28.07.2014 / 11:47
14

A abordagem sed é boa, mas não é possível fazer o loop em todas as linhas. Se você souber quantas linhas deseja manter (para ter um exemplo, eu uso 99 aqui), você pode fazer assim:

sed -i '100,$ d' myfile.txt

Explicação: sed é um processador de expressão regular. Com a opção -i dada, ele processa um arquivo diretamente ("inline") - em vez de apenas lê-lo e gravar os resultados na saída padrão. 100,$ significa apenas "da linha 100 até o final do arquivo" - e é seguido pelo comando d , que você provavelmente adivinhou corretamente para "excluir". Então, em suma, o comando significa: "Excluir todas as linhas da linha 100 para o final do arquivo de myfile.txt". 100 é a primeira linha a ser excluída, pois você deseja manter 99 linhas.

Editar: Se, por outro lado, houver arquivos de log em que você deseja manter, as últimas 100 linhas:

[ $(wc -l myfile.txt) -gt 100 ] && sed -i "1,$(($(wc -l myfile.txt|awk '{print $1}') - 100)) d" myfile.txt

O que está acontecendo aqui:

  • [ $(wc -l myfile.txt) -gt 100 ] : faça o seguinte apenas se o arquivo tiver mais de 100 linhas
  • $((100 - $(wc -l myfile.txt|awk '{print $1}'))) : calcula o número de linhas a serem excluídas (isto é, todas as linhas do arquivo, exceto as (últimas) 100 a serem mantidas)
  • 1, $((..)) d : remova todas as linhas da primeira para a linha calculada

EDITAR: como a questão foi editada para dar mais detalhes, incluirei esta informação adicional com a minha resposta também. Fatos adicionados são:

  • um tamanho específico permanecerá com o arquivo (10.000 bytes)
  • cada linha tem um tamanho específico em bytes (300 bytes no exemplo)

A partir destes dados, é possível calcular o número de linhas a permanecer como "/", que com o exemplo significaria 33 linhas. O termo de shell para o cálculo: $((size_to_remain / linesize)) (pelo menos no Linux usando Bash, o resultado é um inteiro). O comando ajustado agora seria:

# keep the start of the file (OPs question)
sed -i '34,$ d' myfile.txt
# keep the end of the file (my second example)
[ $(wc -l myfile.txt) -gt 33 ] && sed -i "1,33 d" myfile.txt

Como os tamanhos são conhecidos antecipadamente, não há mais necessidade de um cálculo incorporado ao comando sed . Mas, para flexibilidade, dentro de algum script de shell, é possível usar variáveis.

Para processamento condicional com base no tamanho do arquivo, pode-se usar a seguinte estrutura "teste":

[ "$(ls -lk $file | awk ' {print $5}')" -gt 100 ] &&

que significa: "se o tamanho de $file exceder 100kB, faça ..." ( ls -lk lista o tamanho do arquivo em kB na posição 5, portanto awk é usado para extrair exatamente isso).

    
por 24.07.2012 / 10:29
0

Ao não encontrar um comando para fazer isso, eu escrevi um script rápido (não testado):

#!/bin/sh

# Usage: $0 glob.* 25000
# where glob.* is a wildcard pattern and 25000 is the maximum number of bytes.

limit=20000
tmp=/tmp/trim
[[ "$2" == +([0-9]) ]] || limit=$2
limit='expr $len + 1'
for file in $1;
do
    [[ 'wc -c $file' -lt $limit ]] && continue
    head -c $file > $tmp
    sed '$d' $tmp
    $tmp > $file
done
    
por 25.07.2012 / 17:28
-1

Você pode usar o comando linux sed para remover linhas de um arquivo. O comando a seguir exclui a última linha do filename.txt:

sed '$d' filename.txt

Com o awk ou find você pode procurar por um padrão que corresponda ao seu comando sed. Primeiro, você pesquisa com awk ou procura os arquivos que deseja encurtar e, em seguida, pode remover as linhas com sed.

    
por 24.07.2012 / 09:06
-1

Eu fiz algo parecido com o rabo. Para manter apenas as últimas 10.000 linhas neste caso:

TMP=$(tail -n 10000 /path/to/some/file 2>/dev/null) && echo "${TMP}" > /path/to/some/file
    
por 08.04.2016 / 22:43