Exclua os arquivos mais antigos recursivamente até que o limite máximo seja atingido

3

Estou tentando modificar O script de Sergiy para agir de forma recursiva (atualmente apenas remove 1 arquivo, mas eu gostaria que ele removesse todos os arquivos até que o limite máximo de arquivo fosse atingido). Mas eu não consigo descobrir. Eu sinto que deveria ser capaz de modificar a primeira função ("delete oldest") para ler vários timestamps e passar isso para a linha seguinte, mas eu não estou familiarizado com este comando e não consigo encontrar nenhuma informação nele. Qualquer ideia seria apreciada!

    
por MysticEagle 27.10.2017 / 10:08

2 respostas

4

Se você quiser que o script ainda opere em um diretório, sem fazer recursão por meio de subdiretórios, a contagem e a exclusão podem ser feitas em um loop while . A última seção da função main deve ser alterada para ficar assim:

local file_inodes=$(get_files)
    while filecount_above_limit 
    do
        printf  "@@@ File count in %s is above %d." "$directory" $max_files
        printf "Will delete oldest\n"
        sort -k1 -n <<< "$file_inodes" | delete_oldest
        local file_inodes=$(get_files)
    done
    printf "@@@ File count in %s is below %d."  "$directory" $max_files
    printf "Exiting normally"

Aviso!
O problema com essa alteração simples é que, se você não tiver comentado na linha de exclusão na parte superior, o script fará um loop infinitamente, pois recalcula o número de arquivos após cada exclusão. Se um arquivo não for excluído, a contagem de arquivos permanece a mesma e o loop nunca sai.

É possível alterar o script de uma maneira mais complexa, remover o arquivo inode do array file_inodes depois de excluído e incrementar negativamente a variável file_count , em vez de repetir a linha local file_inodes=$(get_files) . Isso lidaria com a situação de verificação não excluída, mas deixarei isso para outra pessoa.

    
por Arronical 27.10.2017 / 10:46
2

Eu sugeriria outra solução, que andará recursivamente dentro da estrutura da árvore de diretórios de destino e excluirá todos os arquivos, exceto um número predefinido dos novos arquivos. Essa solução é baseada em: (1) Script bash recursivo e (2) Explicando um script de shell para imprimir recursivamente a árvore de diretórios completa .

1. Crie um arquivo de script executável, chamado walkr ( caminhar e remover ), localizado em /usr/local/bin para ser acessível como comando shell ( etapas mais detalhadas ).

2. O conteúdo do script walkr é bastante simples:

#!/bin/bash

[[ -z "${NFK}" ]] && NFK='7' || NFK="$NFK"
[[ -z "" ]] && ABS_PATH="${PWD}" || cd "" && ABS_PATH="${PWD}"

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally
        rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
}

walk() {
        cd "" && file_operations # Change directory to the destination path and call the above function
        for item in ""/*; do [[ -d "$item" ]] && walk "$item"; done # Make the recursion
}

walk "${ABS_PATH}"

3. Explicação:

  • No início, o script verificará se a variável $NFK (que determina o número de arquivos a serem mantidos) é definida antecipadamente - a condição [[ -z "${NFK}" ]] . Se não estiver definido, o valor padrão será 7 .

  • Em seguida, o script lida com o caminho de destino (stdin do comando). Se não for fornecido - a condição [[ -z "" ]] - o script funcionará no diretório atual.

  • Finalmente, a função principal walk() será executada.

  • A função walk() :

    • Inicialmente, o diretório será alterado para o caminho de destino cd "" e, em seguida, chamará a função file_operations() , que funcionará dentro dela.

    • Além disso, para cada $item , dentro do diretório atual ""/* , que também é diretório [[ -d "$item" ]] , a função walk() será executada novamente, portanto, criamos a recursão .

  • A função file_operations() :

    • Inicialmente, ele definirá um valor local da variável interna de Bash $IFS , assim podemos manipular corretamente <the list of the files to be removed> , não importa se há espaços dentro os nomes de arquivos separados.

    • Além disso, o comando rm $(<the list of the files to be removed>) será executado. O redirecionamento dos erros 2>/dev/null é para esses casos quando não há nada para remover. <the list of the files to be removed> é obtido desta forma:

      • O comando ls -lt listará o conteúdo do diretório atual com o formato de listagem longa -l e a lista será classificada por hora de modificação, o mais novo primeiro -t . E essa lista é canalizada em | para o próximo comando.

      • O próximo comando grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' irá cortar essas linhas que começam com ^ com - , desde o início até o padrão [0-9]{2}:[0-9]{2}_ . A opção -P com a opção -o exibirá as sequências que correspondem ao padrão ^-.*[0-9]{2}:[0-9]{2}_ . O \K notify irá ignorar a parte correspondente antes de si. ( fonte - esta resposta útil )

        Assim, obteremos apenas os nomes dos arquivos da lista. Na saída de ls -l , as linhas que descrevem os diretórios começam com d e, para os arquivos, começam com - . ( fonte da ideia )

        Este padrão corresponde ao formato de hora 00:00 .

      • Finalmente, o comando tail -n +"$((NFK+1)) irá cortar as primeiras linhas da nossa lista de arquivos. O número dessas primeiras linhas é igual ao valor de $NFK plus 1, este é um requisito do comando tail .

4. Exemplos de uso:

  • Para executar walkr no diretório atual:

    walkr      # You shouldn't use any argument, 
    walkr ./   # but you can use also this format
    
  • Para executar walkr em qualquer diretório filho:

    walkr <directory name>
    walkr ./<directory name>
    walkr <directory name>/<sub directory>
    
  • Para executar walkr para qualquer outro diretório:

    walkr /full/path/to/<directory name>
    
  • Para alterar o número de arquivos a serem mantidos (para 3 , por exemplo), use este formato

    NFK=3 walkr
    NFK=3 walkr /full/path/to/<directory name>
    # etc.
    

5. Vamos brincar com o script walkr :

  • Podemos usar o comando touch file.name -d "1 hour ago" para criar um arquivo vazio com a data de uma hora atrás. Assim, podemos usar os seguintes comandos para criar uma estrutura de diretórios como esta apresentada aqui .

    mkdir -p ~/temp/dir{A..C} && cd ~/temp ;\
    DEST='';      touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirA/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirB/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago" ;\
    DEST='dirC/'; touch ${DEST}new_file{1..7} && touch ${DEST}older_file{1..7} -d "1 hour ago" && touch ${DEST}oldest_file{1..7} -d "2 hour ago"
    
  • Agora podemos realizar alguns testes:

Atualização da funcionalidade do script. Aqui é apresentada a versão atualizada do script acima:

#!/bin/bash

[[ -z "" ]] && ABS_PATH="${PWD}" || cd "" && ABS_PATH="${PWD}"
[[ -z "" ]] && NFK='7' || NFK=""  # Number of the files to be kept
[[ -z "" ]] && REC='1' || REC=""  # REC='1' - work recursively
[[ -z "" ]] && VRB='1' || VRB=""  # VRB='1' - work in verbose mode

file_operations() {
        local IFS=$'\t\n' # Change the value of the Internal Field Separator locally

        if [ "$VRB" == "1" ]
        then # Verbose mode:
                rm -v $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null && printf " -from: '%s' \n" "" || echo "nothing to remove in: ''"
        else # Quiet mode:
                rm $(ls -lt | grep -Po '^-.*[0-9]{2}:[0-9]{2} \K.*' | tail -n +"$((NFK+1))") 2>/dev/null
        fi
}

walk() {
        # Change directory to the destination path and call the above function, pass  for the verbose mode
        cd "" && file_operations ""
        # If REC='1': Recursive mode -- Make the recursion; otherwise work on the curent level
        if [ "$REC" == "1" ]; then for item in ""/*; do [[ -d "$item" ]] && walk "$item"; done; fi
}

walk "${ABS_PATH}"
  • Esta versão do script pode lidar com mais algumas variáveis de entrada. Ele tem os modos quiet e verbose e pode funcionar de forma não recursiva.

  • O formato completo é:

    walkr '<destination path>' '<number of lines to be kept>' '<no recursion>' '<quiet>'
    

    Onde o conteúdo exato de <no recursion> e <quiet> não tem importância. Apenas as variáveis de entrada e não devem estar vazias para serem sobrescritas pelo comportamento padrão.

por pa4080 27.10.2017 / 18:10