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?
@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
dividir nomes de arquivos com espaço em branco e
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.
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
-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.
#! /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
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.
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.
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".
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:
Respostas:
Para uma solução portátil e confiável, tente o seguinte:
A
tail -n -10
sintaxe 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 comandorm
corre o risco dexargs
corrige 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:
Isso será um pouco mais lento porque gera uma separação
rm
para cada arquivo, mas ainda evitará as advertências 1 e 2 acima (mas ainda sofre com novas linhas nos nomes dos arquivos).fonte
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
head
exargs
dê erros. As opções corretas sãohead -n 10
exargs rm -f
. O comando completo éls -1tr ./ | head -n 10 | xargs rm -rf
ls -1tr
número UM não é a letra "L".O código que você deseja incluir no seu script é
A opção
-1
(numérica) imprime cada arquivo em uma única linha, para segurança. A-f
opção pararm
diz para ignorar arquivos inexistentes para quandols
não retornar nada.fonte
/path/to/your/logs
é apenas para você entrar no caminho correto. Para encontrar o bug executar as partesls -t /path/...
els -t /path/... | tail -n +11
sozinho e verificar se o resultado está correto.+
antes do número. Eletail
nã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.ls
imprime um nome de arquivo, não um caminho para que o seurm -f
vai tentar remover os arquivos no diretório atualAparentemente, analisar
ls
é mau .Se cada arquivo for criado diariamente e você quiser manter os arquivos criados nos últimos 10 dias, poderá:
Ou se cada arquivo for criado arbitrariamente:
fonte
-mtime 10 -delete
exclui apenas arquivos modificados exatamente 10 dias atrás. Arquivos mais antigos não serão excluídos.-mtime +11 -delete
excluirá os arquivos que foram modificados há 11 ou mais dias, deixando os últimos 10 dias de arquivos de log.%p
vez de%P
é necessário noprintf
para que os arquivos cheguem tenham o caminho completo. Caso contrário, orm -rf
não funcionará.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.
fonte
Eu modifiquei a abordagem de Isaac um pouco.
Agora ele funciona com o caminho desejado:
fonte
A partir daqui :
fonte
No Bash, você pode tentar:
Isso pula os 10 als mais novos exclui o restante. logrotate pode ser melhor, eu só quero corrigir as respostas erradas relacionadas ao shell.
fonte
Não tenho certeza se isso ajudará alguém, mas para evitar possíveis problemas com caracteres instáveis, usei apenas
ls
para executar a classificação, mas depois use os arquivosinode
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 {} \;
fonte
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:
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".
Explicação:
ls -td *.tar
lista 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 linhasprint "rm \"" $0 "\""
constrói uma linha: rm "nome do arquivo"eval
executaComo estamos usando
rm
, eu não usaria o comando acima em um script! O uso mais sábio é:No caso de usar o
ls -t
comando, não haverá nenhum dano em exemplos tolos como:touch 'foo " bar'
etouch '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:
para definir a variável
VarName
para o valor de$1
. Várias variáveis podem ser criadas de uma só vez. IssoVarName
se 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:fonte
Eu concordo que analisar sl é mau!
Verifique se apenas os últimos 5 arquivos são mantidos no
/srv/backups
caminho.fonte
Com base na resposta de Isaac Freeman, mas trabalhando em qualquer diretório:
Você também pode definir um prefixo, como
$PATH_TO_DELETE/testFi*
Se você usar
*
após oPATH
ls
comando, exibirá nomes de arquivos absolutos, pelo menos no "meu" bash :)fonte