Como excluo todos os 10 arquivos mais recentes no Linux?

41

Estou tentando manter um diretório cheio de arquivos de log gerenciáveis. Todas as noites, quero excluir todos, menos os 10 mais recentes. Como posso fazer isso em um único comando?

    
por user75814 08.04.2011 / 16:22

9 respostas

50

Para uma solução portátil e confiável, tente o seguinte:

ls -1tr | head -n -10 | xargs -d '\n' rm -f --

A sintaxe tail -n -10 em uma das outras respostas parece não funcionar em todos os lugares (ou seja, não em meus sistemas RHEL5).

E usar $() ou '' na linha de comando de rm corre o risco de

  1. dividindo nomes de arquivos com espaço em branco e
  2. excedendo o limite máximo de caracteres da linha de comando.

xargs corrige esses dois problemas, pois descobrirá automaticamente quantos argumentos ele pode passar dentro do limite de caracteres e, com o -d '\n' , somente será dividido no limite da linha da entrada. Tecnicamente, isso ainda pode causar problemas para nomes de arquivos com uma nova linha, mas isso é muito menos comum que nomes de arquivos com espaços, e a única maneira de contornar as novas linhas seria muito mais complicada, provavelmente envolvendo pelo menos awk, se não perl.

Se você não tem xargs (antigos sistemas AIX, talvez?), você pode fazer um loop:

ls -1tr | head -n -10 | while IFS= read -r f; do
  rm -f "$f"
done

Isso será um pouco mais lento porque gera rm separado para cada arquivo, mas ainda evitará as advertências 1 e 2 acima (mas ainda sofrerá de novas linhas nos nomes dos arquivos).

    
por 19.02.2013 / 23:31
19

O código que você deseja incluir no seu script é

 rm -f $(ls -1t /path/to/your/logs/ | tail -n +11)

A opção -1 (numérico) imprime cada arquivo em uma única linha, por segurança. A opção -f para rm diz para ignorar arquivos inexistentes para quando ls não retorna nada.

    
por 08.04.2011 / 16:25
10

Aparentemente analisando ls é mau .

Se cada arquivo for criado diariamente e você quiser manter os arquivos criados nos últimos 10 dias, faça o seguinte:

find /path/to/files -mtime 10 -delete

Ou se cada arquivo for criado arbitrariamente:

find /path/to/files -maxdepth 1 -type f -printf '%Ts\t%P\n' | sort -n | head -n -10 | cut -f 2- | xargs rm -rf
    
por 28.01.2014 / 16:29
9

Uma ferramenta como o logrotate faz isso para você. Isso torna o gerenciamento de logs muito mais fácil. Você também pode incluir rotinas de limpeza adicionais como o halo sugerido.

    
por 08.04.2011 / 19:25
2

Eu modifiquei a abordagem de Isaac um pouco.

Agora funciona com um caminho desejado:

ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
    
por 21.10.2014 / 14:51
1

De aqui :

#! /bin/sh
# keepnewest
#
# Simple directory trimming tool to handle housekeeping
# Scans a directory and deletes all but the N newest files
#
# Usage: cleanup <dir> <number of files to keep>
#
# v 1.0 Piers Goodhew 1/mar/2007. No rights retained.


if [ $# -ne 2 ]; then
  echo 1>&2 "Usage: $0 <dir> <number of files to keep>"
  exit 1
fi

cd $1
files_in_dir='ls | wc -l'
files_to_delete='expr $files_in_dir - $2'
if [ $files_to_delete -gt 0 ]; then
  ls -t | tail -n $files_to_delete | xargs rm
  if [ $? -ne 0 ]; then
    echo "An error ocurred deleting the files"
    exit 1
  else
    echo "$files_to_delete file(s) deleted."
  fi
else
  echo "nothing to delete!"
fi
    
por 21.03.2013 / 04:40
1

Não tenho certeza de que isso ajudará a ninguém, mas para evitar possíveis problemas com caracteres estranhos, usei apenas ls para executar a classificação, mas depois use os arquivos inode para a exclusão.

Para o diretório atual, isso mantém os 10 arquivos mais recentes com base no tempo de modificação.

ls -1tri | awk '{print $1}' | head -n -10 | xargs -n1 -t -Iinode find . -inum inode -exec rm -i {} \;

    
por 19.05.2017 / 03:30
0

No Bash, você pode tentar:

ls -1t | (i=0; while read f; do
  if [ $i -lt 10 ]; then
    ((i++))
    continue
  else
    rm -f "$f"
  fi
done)

Isso pula as 10 mais novas als exclui o resto. logrotate pode ser melhor, eu só quero corrigir as respostas erradas relacionadas ao shell.

    
por 20.02.2013 / 01:18
0

Eu precisava de uma solução elegante para o busybox (roteador), todas as soluções xargs ou array eram inúteis para mim - nenhum comando desse tipo estava disponível lá. find e mtime não é a resposta adequada, pois estamos falando de 10 itens e não necessariamente 10 dias. A resposta de Espo foi a mais curta e mais limpa e provavelmente a mais simples.

Erro com espaços e quando nenhum arquivo deve ser excluído, ambos são simplesmente resolvidos da maneira padrão:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

Versão mais educativa: podemos fazer tudo se usarmos o awk de forma diferente. Normalmente, eu uso esse método para passar (retornar) variáveis do awk para o sh. Como lemos o tempo todo que não pode ser feito, eu discuto: aqui está o método.

Exemplo para arquivos .tar sem problemas com relação aos espaços no nome do arquivo. Para testar, substitua "rm" pelo "ls".

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

Explicação:

ls -td *.tar lista todos os arquivos .tar classificados pelo tempo. Para aplicar a todos os arquivos na pasta atual, remova a parte "d * .tar"

awk 'NR>7... ignora as primeiras 7 linhas

print "rm \"" $0 "\"" constrói uma linha: rm "nome do arquivo"

eval executa

Como estamos usando rm , eu não usaria o comando acima em um script! O uso mais sábio é:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

No caso de usar o comando ls -t , isso não causará nenhum dano em exemplos bobos como: touch 'foo " bar' e touch 'hello * world' . Não que nós nunca criemos arquivos com esses nomes na vida real!

Sidenote. Se quiséssemos passar uma variável para o sh dessa forma, simplesmente modificaríamos a impressão:

print "VarName="$1

para definir a variável VarName com o valor de $1 . Múltiplas variáveis podem ser criadas de uma só vez. Este VarName se torna uma variável sh normal e pode ser normalmente usado em um script ou shell depois. Então, para criar variáveis com o awk e devolvê-las ao shell:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName="$1 }'); echo "$VarName"
    
por 03.10.2018 / 19:57

Tags