Como apagar arquivos de uma pasta com mais de 60 arquivos no unix?

7

Eu quero colocar um script no cronjob que será executado em um determinado momento e se a contagem de arquivos for maior que 60, ele excluirá os arquivos mais antigos dessa pasta. Ultimo a entrar primeiro a sair. Eu tentei,

#!/bin/ksh  
for dir in /home/DABA_BACKUP  
do  
    cd $dir  
    count_files='ls -lrt | wc -l'   
    if [ $count_files -gt 60 ];  
    then  
        todelete=$(($count_files-60))  
        for part in 'ls -1rt'  
        do  
            if [ $todelete -gt 0 ]  
            then  
                rm -rf $part  
                todelete=$(($todelete-1))  
            fi  
        done  
    fi
done   

Estes são todos os arquivos de backup que são salvos diariamente e denominados backup_$date . Tudo bem?

    
por pmaipmui 14.06.2015 / 13:38

5 respostas

3

Não, por um lado, será quebrado em nomes de arquivos contendo novas linhas. Também é mais complexo do que o necessário e tem todos os perigos de analisar o ls .

Uma versão melhor seria (usando ferramentas GNU):

#!/bin/ksh  
for dir in /home/DABA_BACKUP/*
do
    ## Get the file names and sort them by their
    ## modification time
    files=( "$dir"/* );
    ## Are there more than 60?
    extras=$(( ${#files[@]} - 60 ))
    if [ "$extras" -gt 0 ]
    then
    ## If there are more than 60, remove the first
    ## files until only 60 are left. We use ls to sort
    ## by modification date and get the inodes only and
    ## pass the inodes to GNU find which deletes them
    find dir1/ -maxdepth 1 \( -inum 0 $(\ls -1iqtr dir1/ | grep -o '^ *[0-9]*' | 
        head -n "$extras" | sed 's/^/-o -inum /;' ) \) -delete
    fi
done

Observe que isso pressupõe que todos os arquivos estão no mesmo sistema de arquivos e podem fornecer resultados inesperados (como excluir arquivos errados), se não estiverem. Também não funcionará bem se houver vários hardlinks apontando para o mesmo inode.

    
por 14.06.2015 / 14:13
3
#! /bin/zsh -
for dir (/home/DABA_BACKUP/*) rm -f $dir/*(Nom[61,-1])

Para o zsh-ignorant ; -):

  • for var (list) cmd : versão curta do ciclo for var in list; do cmd; done (que lembra a sintaxe perl ).
  • As variáveis $dir : zsh não precisam ser citadas como em outros shells, pois os operadores zsh têm explícito split e glob , portanto, não fazem divisão implícita + glob na expansão de parâmetros.
  • *(...) : glob com qualificadores glob :
  • N : nullglob : a glob expande-se para nada em vez de gerar um erro quando não corresponde.
  • m : o rder os arquivos gerados no m tempo de odificação (o mais novo primeiro).
  • [61,-1] : a partir dessa lista ordenada, escolha o 61º para o último.

Então, basicamente, remove todos, exceto os 60 arquivos mais novos.

    
por 15.06.2015 / 00:01
1

Para obter uma lista das entradas mais antigas a serem excluídas (mantendo assim as 60 últimas entradas):

ls -t | tail -n +61

Observe que o principal problema da sua abordagem ainda precisa ser tratado aqui: como lidar com arquivos com novas linhas, caso isso seja importante; caso contrário, você pode simplesmente usar (substituindo seu programa bastante complexo):

cd /home/DABA_BACKUP || exit 1
ls -t | tail -n +61 | xargs rm -rf


Nota: Como parece que você tem backups diários , você poderia também usar uma abordagem baseada nas datas do arquivo e find ; como em:

find /home/DABA_BACKUP -mtime +60 -exec ls {} +

(onde o comando ls iria - após um exame cuidadoso da operação correta! - ser substituído pelo comando rm apropriado).

    
por 14.06.2015 / 14:09
1
rm60()( IFS=/; set -f; set $(
        set +f; \ls -1drt ./*)
        while shift &&
              [ $# -gt 60 ]
        do    [ -d "${1%?.}" ] ||
              rm "./${1%?.}"   || exit
        done
)

Isso funcionará para você. Ele excluirá os arquivos mais antigos no diretório atual até uma contagem de 60. Ele fará isso por analisando ls robustamente e fará isso sem fazer nenhuma suposição sobre os seus nomes de arquivos - eles podem ter qualquer nome e não precisam ser nomeados por datas. Isso só funcionará para uma listagem do diretório atual e, no caso de você ter um POSIX ls instalado (e não mascarado por alguma função maligna do shell, mas alias es são cobertos) .

A solução acima aplica apenas uma divisão de shell muito básica em alguns nomes de caminho muito básicos do Unix. Isso garante que ls liste todos os arquivos não pontuados no diretório atual, um por linha, como:

./oldestfile
./second-oldestfile

Agora, qualquer um desses pode ter novas linhas entre eles também, mas isso não seria um problema. Porque nesse caso eles seriam listados como:

./oldest
file
./s
econd

old
est
file
./third

... e assim por diante. E as novas linhas não nos incomodam de qualquer maneira - porque nós não nos separamos delas. Por que nós? Estamos trabalhando com nomes de caminho, devemos dividir no delimitador de caminho, e é isso que fazemos: IFS=/ .

Agora isso funciona um pouco estranho. Acabamos com uma lista de argumentos que se parece com isso:

<.> <file1\n.> <file2\n.> ... <filelast>

... mas isso é realmente muito bom para nós, porque podemos atrasar nossos argumentos sendo tratados pelo shell como arquivos (ou, no caso, queremos evitar, symlinks) até que nós ' está bastante pronto para rm deles.

Então, assim que tivermos nossa lista de arquivos, tudo o que temos a fazer é shift eliminar nosso primeiro argumento, verificar se atualmente temos mais de 60 argumentos, provavelmente recusar rm um diretório filho (embora, é claro, isso é completamente com você) , e de outra forma rm nosso primeiro argumento menos seus dois últimos caracteres. Nós não temos que nos preocupar com o último último argumento - que não tem o período acrescentado - porque nós nunca chegamos lá, e ao invés disso paramos aos 60. Se nós chegarmos até aqui uma iteração, então nós apenas tentamos novamente e passamos o loop da lista de argos até que tenhamos podado para nossa satisfação.

Como isso quebra? Não, ao meu conhecimento, mas eu permiti - se a qualquer momento ocorrer um erro inesperado, o loop será interrompido e a função retornará diferente de 0.

E assim ls pode fazer sua listagem para você no diretório atual sem nenhum problema. Você pode permitir que ele classifique seus argumentos para você, contanto que você possa delimitá-los de maneira confiável. É por essa razão que isso não funcionará como escrito para nada além do diretório atual - mais de um delimitador em um pathstring exigiria outro nível de delimitação, o que poderia ser feito considerando-o duplamente para todos, exceto o último em campos NUL. , mas eu não me importo de fazer isso agora.

    
por 14.06.2015 / 23:50
-1

Se você sabe, os arquivos são todos chamados de backup_ *, você deve incluir isso no comando ls, para que você possa manipulá-los apenas, e não os arquivos que caiam acidentalmente no diretório. Então ls é usado em um pipe, ele lista apenas 1 arquivo por linha, e então só contando, não precisa classificar, então

count_files=$(ls -U backup_* | wc -l)

e

for part in $(ls -rt backup_*);do
    rm -rf "$part"
    todelete=$(($todelete-1))
    if [[ $todelete -eq 0 ]]; then
        break
    fi
done
    
por 14.06.2015 / 14:28