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?
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?
Respostas:
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
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).
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
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
ls -1trnúmero UM não é a letra "L".
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.
/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.
+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.
lsimprime um nome de arquivo, não um caminho para que o seu rm -fvai tentar remover os arquivos no diretório atual
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
-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.
%pvez de %Pé necessário no printfpara que os arquivos cheguem tenham o caminho completo. Caso contrário, o rm -rfnão funcionará.
Eu modifiquei a abordagem de Isaac um pouco.
Agora ele funciona com o caminho desejado:
ls -d -1tr /path/to/folder/* | head -n -10 | xargs -d '\n' rm -f
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
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.
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 {} \;
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"
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 :)