Como eu excluo todos os 10 arquivos mais recentes no Linux?


49

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


3
ter um olhar para logrotate
knittl


@michael Como o meu pode ser uma duplicata desse, se minha discussão foi criada primeiro?
Dev4life 06/07/19

@ovaherenow "sua"? Não vejo seu nome em nenhuma das perguntas, então não tenho certeza do que você quer dizer. Meu comentário nem indica qual é uma duplicata da outra. Mas isso não importa - é apenas um comentário, com um link. Pode ser adicionado a uma ou a ambas as perguntas. Cabe a outra pessoa - um administrador - marcar um ou outro como duplicado. Nenhum truque nefasto foi planejado nem deve ser percebido. Mais importante do que qual pergunta foi a primeira, é qual pergunta tem a melhor resposta - novamente, o link facilita essa discussão, não determina a resposta.
22617 Michael Michael

@michael wow. Isso é realmente estranho. Esse tópico foi aberto por mim, mas obviamente esse não é o meu nome de usuário. Mas estou sendo notificado sobre esse tópico.
Dev4life # 12/17

Respostas:


58

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

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

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

E usar $()ou ``na linha de comando rmcorre o risco de

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

xargscorrige esses dois problemas, porque ele descobre automaticamente quantos argumentos pode passar dentro do limite de caracteres e, com o -d '\n'mesmo, será dividido apenas no limite da linha da entrada. Tecnicamente, isso ainda pode causar problemas para os nomes de arquivos com uma nova linha, mas isso é muito menos comum que os 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 possui xargs (sistemas AIX antigos, talvez?), 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 uma separação rmpara cada arquivo, mas ainda evitará as advertências 1 e 2 acima (mas ainda sofre com novas linhas nos nomes dos arquivos).


@HaukeLaging: A partir da head(1)página de manual:-n, --lines=[-]K print the first K lines instead of the first 10; with the leading '-', print all but the last K lines of each file
Isaac Freeman

No Solaris 10 heade xargsdê erros. As opções corretas são head -n 10e xargs rm -f. O comando completo éls -1tr ./ | head -n 10 | xargs rm -rf
DmitrySandalov

1
Observe que o ls -1trnúmero UM não é a letra "L".
usuário shonky linux

1
Também deve ser observado que as novas linhas são caracteres raros, mas válidos, nos nomes de arquivos ext. Isso significa que se você tiver os arquivos "that" e "that \ nthing", se esse script clicar em "that \ nthing", ele excluirá "that", erro quando não puder excluir "coisa" e sairá "esse \ nthing" (o destino pretendido) não é afetado.
Synthead

1
@IsaacFreeman Excluí meus maus conselhos, felicidades.
Walf

20

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érica) imprime cada arquivo em uma única linha, para segurança. A -fopção para rmdiz para ignorar arquivos inexistentes para quando lsnão retornar nada.


Pôster original, aqui. Eu tentei este comando, mas ele não funciona - eu recebo o erro "rm: operando em falta"
user75814

2
Você usou o caminho correto? Obviamente, /path/to/your/logsé apenas para você entrar no caminho correto. Para encontrar o bug executar as partes ls -t /path/...e ls -t /path/... | tail -n +11sozinho e verificar se o resultado está correto.
Daniel Böhmer

1
@HaukeLaging Tenho certeza de que você entendeu algo errado. Cuidado com o +antes do número. Ele tailnão imprime os últimos n elementos, mas todos os elementos que começam no n'ésimo. Fiz o check-in novamente e o comando definitivamente deve remover apenas elementos com mais de 10 arquivos mais recentes em todas as circunstâncias.
Daniel Böhmer 14/03

@halo De fato, desculpe.
Hauke ​​Laging 14/03/2013

2
Eu acho que sua resposta é muito perigosas lsimprime um nome de arquivo, não um caminho para que o seu rm -fvai tentar remover os arquivos no diretório atual
Jürgen Steinblock

14

Aparentemente, analisar lsé mau .

Se cada arquivo for criado diariamente e você quiser manter os arquivos criados nos últimos 10 dias, poderá:

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

5
-mtime 10 -deleteexclui apenas arquivos modificados exatamente 10 dias atrás. Arquivos mais antigos não serão excluídos. -mtime +11 -deleteexcluirá os arquivos que foram modificados há 11 ou mais dias, deixando os últimos 10 dias de arquivos de log.
Markus

1
Eu acho que o uso de em %pvez de %Pé necessário no printfpara que os arquivos cheguem tenham o caminho completo. Caso contrário, o rm -rfnão funcionará.
jalopaba 5/09

9

Uma ferramenta como o logrotate faz isso por você. Isso facilita muito o gerenciamento de logs. Você também pode incluir rotinas de limpeza adicionais, como sugerido o halo.



1

A partir daqui :

#! /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

1
Obrigado por fornecer esta resposta. Embora fornecer um código como esse seja útil, também ajuda a fornecer algumas informações adicionais sobre como ele pode ser usado ou o que o código faz. Você pode usar o botão de edição para fazer melhorias futuras em sua postagem.
Nhinkle

1

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 os 10 als mais novos exclui o restante. logrotate pode ser melhor, eu só quero corrigir as respostas erradas relacionadas ao shell.


1

Não tenho certeza se isso ajudará alguém, mas para evitar possíveis problemas com caracteres instáveis, usei apenas lspara executar a classificação, mas depois use os arquivos inodepara 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 {} \;


0

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

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

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

Versão um pouco mais educacional: podemos fazer tudo se usarmos o awk de maneira diferente. Normalmente, eu uso esse método para passar (retornar) variáveis ​​do awk para o sh. Enquanto lemos o tempo todo que não pode ser feito, imploro para diferir: aqui está o método.

Exemplo para arquivos .tar sem problemas em 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 *.tarlista todos os arquivos .tar classificados por hora. Para aplicar a todos os arquivos na pasta atual, remova a parte "d * .tar"

awk 'NR>7... pula as 7 primeiras 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 ls -tcomando, não haverá nenhum dano em exemplos tolos como: touch 'foo " bar'e touch 'hello * world'. Não que nós sempre criamos arquivos com esses nomes na vida real!

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

print "VarName="$1

para definir a variável VarNamepara o valor de $1. Várias variáveis ​​podem ser criadas de uma só vez. Isso VarNamese torna uma variável sh normal e pode ser normalmente usada em um script ou shell posteriormente. Portanto, para criar variáveis ​​com o awk e devolvê-las ao shell:

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

0

Eu concordo que analisar sl é mau!

Verifique se apenas os últimos 5 arquivos são mantidos no /srv/backupscaminho.

find /srv/backups -maxdepth 1 -type f -printf '%Ts\t%P\n' \
    | sort -rn \
    | tail -n +6 \
    | cut -f2- \
    | xargs -r r

0

Com base na resposta de Isaac Freeman, mas trabalhando em qualquer diretório:

ls -1tr $PATH_TO_DELETE/* | head -n -10 | xargs -d '\n' rm -f --

Você também pode definir um prefixo, como $PATH_TO_DELETE/testFi*

Se você usar *após o PATH lscomando, exibirá nomes de arquivos absolutos, pelo menos no "meu" bash :)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.