
Eu tenho um script onde desejo limpar arquivos antigos quando há mais do que um limite definido.
Eu tenho este comando:
/bin/rm -f `/bin/ls -t $bkup_p/mysql.daily/* 2> /dev/null | /bin/awk 'NR>'5`
que funciona e como pode haver um espaço em $bkup_p, tentei alterá-lo para
/bin/rm -f `/bin/ls -t "$bkup_p/mysql.daily/*" 2> /dev/null | /bin/awk 'NR>'5`
Mas isso não funciona. Não mostra os arquivos em questão, apenas está vazio
Responder1
Bash não é muito útil quando se trata de filtrar arquivos no momento da modificação e executar comandos neles.
Eu recomendaria tentar o z-shell, então primeiro execute zsh
e depois
rm -i -- "$bkup_p"/mysql.daily/*(DN.Om[1,5])
Isso remove os cinco arquivos simples mais antigos do diretório fornecido, o que suspeito ser provavelmente o que você está tentando alcançar. Obviamente, no final do dia, mude rm -i
para rm -f
se necessário.
Para remover cinco arquivos mais recentes, faça
rm -i -- "$bkup_p"/mysql.daily/*(DN.om[1,5])
Agora, como funciona. Tudo dentro ()
são chamados de qualificadores glob, que basicamente filtram os arquivos dependendo de suas necessidades:
D
inclua dotfiles (aqueles que começam com.
)N
não reporte erro se não houver correspondência.
seleciona apenas arquivos simplesom
classifica na hora da modificação (Om
classifica na ordem inversa)[1,5]
seleciona apenas cinco arquivos da lista
Acredito que tudo isso deve funcionar mesmo com caracteres especiais em nomes de arquivos (espaços, novas linhas, etc.)
Responder2
Como já foi dito, você não deve usar ls
para isso. Mas se o seu ambiente não tiver nada mais sensato e vocêconheça as advertências, vocêpodersaia impune, se os nomes dos seus arquivos forembom o suficiente: nenhum espaço em branco ou caracteres não imprimíveis e, especialmente, nada que o shell interprete como um caractere globbing (pelo menos ?*[]
).
Usar algo como Perl para classificar os arquivos ou colocar as datas nos nomes dos arquivos para que você possa depender da ordem global seria melhor.
Dito isso: no seu segundo trecho, o asterisco está entre aspas, para que o globbing não aconteça. Você veria um erro sobre some dir/mysql.daily/*
não existir, mas não o fez, pois redirecionou a saída do erro.
Nomes de arquivos com espaços causarão o problema de que a saída da substituição do comando é dividida entre os espaços, então algo como some dir/foo
será fornecido rm
em duas partes: some
e dir/foo
. Esperamos que nenhum deles exista, já que seriam removidos.
Se apenas o nome do seu diretório contiver espaços, você poderá cd
acessá-lo primeiro. Ou defina IFS
para conter apenas uma nova linha (em vez do espaço padrão, tabulação, nova linha). Você mencionou um NAS, então é provável que você tenha o busybox. Isso deve funcionar tanto no busybox quanto no bash, e imprimir os nomes dos arquivos, um por linha:
IFS=$'\n'
printf "%s\n" $( ls -t "$bkup_p/mysql.daily/"* | tail -n +6 )
Substitua printf "%s\n"
por rm
para remover os arquivos, se tiver certeza de que funciona corretamente.
Responder3
Eu recomendaria não analisar a saída, ls
pois ela não formata corretamente a saída para canalizar para um novo comando e tem problemas de portabilidade.
Eu tentaria algo assim em vez disso
find $bkup_p/mysql.daily/ -type f -a -mtime +7 -a -name "*.sql" -a -exec rm -f {} +
observação:
"*.sql"
altere isso conforme necessário-mtime +7
significa *se este arquivo foi modificado há mais (+) de 7 dias, obviamente altere isso conforme necessário também
No caso de você querer ter sempre os 10 arquivos mais recentes - não importa o que aconteça (e você tem GNU find
), você pode tentar
find $bkup_p/mysql.daily/ -maxdepth 1 -type f -a -printf "%T+\t%p\n" | sort -r | sed -n '10,$p' | awk '{print $2}' | xargs rm -f
para obter mais informações sobre como formatar find
a saída com o -printf
operador, consulte
man find | less '+/^\s*-printf'
Responder4
O problema de *
não funcionar é porque quando você cita, ele não expande.
Compare os resultados disto: echo *
com isto: echo "*"
.
A solução robusta é usar um array, um arquivo em cada posição do array.
Se você tiver sorte o suficiente e encontrar suporte para isto -printf
:
dir="$bkup_p/mysql.daily/"
find "$dir" -printf '%T@:%i\n'
Todos os nomes de arquivos serão evitados. Cada arquivo será uma linha. E uma linha apenas de números, um ponto opcional e dois pontos.
Isso poderia ser facilmente resolvido:
find "$dir" -printf '%T@:%i\n' | sort -rn
A matriz mais simples são os argumentos posicionais.
Isso definirá todos os arquivos do diretório para a lista de argumentos posicionais:
set -f; set -- $(find "$dir" -printf '%T@:%i\n' | sort -n )
O set -f evitará a expansão de qualquer nome de arquivo que também seja um curinga *
. Para evitar (remover) os 6 primeiros nomes de arquivos, apenas shift
eles:
shift 5
E faça o que for necessário com os arquivos restantes:
for f
do rm -f "$(find "$dir" -inum "${f#*:}")"
done
Todo o roteiro.
Todo o script (robusto para qualquer nome de arquivo) se torna:
dir="$bkup_p/mysql.daily/"
set -f
set -- $(find "$dir" -type f -printf '%T@:%i\n'|sort -rn)
shift 5
for f
do rm -f "$(find "$dir" -inum "${f#*:}")"
done