Todo o roteiro.

Todo o roteiro.

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 zshe 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 -ipara rm -fse 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:

  • Dinclua dotfiles (aqueles que começam com .)
  • Nnão reporte erro se não houver correspondência
  • .seleciona apenas arquivos simples
  • omclassifica na hora da modificação ( Omclassifica 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 lspara 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/fooserá fornecido rmem duas partes: somee dir/foo. Esperamos que nenhum deles exista, já que seriam removidos.

Se apenas o nome do seu diretório contiver espaços, você poderá cdacessá-lo primeiro. Ou defina IFSpara 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 rmpara remover os arquivos, se tiver certeza de que funciona corretamente.

Responder3

Eu recomendaria não analisar a saída, lspois 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 +7significa *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 finda saída com o -printfoperador, 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 shifteles:

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

informação relacionada