Como apagar o diretório mais antigo em um determinado diretório? [duplicado]

8

Eu tenho um diretório de backup que armazena x de outros diretórios que exigem backup. Eu preciso de algo que será executado antes de outro diretório é movido para o backup, que irá verificar se o número de diretórios atingiu x e se tiver, ele irá excluir o diretório mais antigo.

Isso deve ser feito em um script bash .

    
por AAaa 12.01.2012 / 12:52

3 respostas

19

Analisando a saída de ls não é confiável .

Em vez disso, use find para localizar os diretórios e sort para encomendá-los por timestamp. Por exemplo:

IFS= read -r -d $'
#!/usr/bin/env bash

dir="$1"
min_dirs=3

[[ $(find "$dir" -maxdepth 1 -type d | wc -l) -ge $min_dirs ]] &&
IFS= read -r -d $'
IFS= read -r -d $'
#!/usr/bin/env bash

dir="$1"
min_dirs=3

[[ $(find "$dir" -maxdepth 1 -type d | wc -l) -ge $min_dirs ]] &&
IFS= read -r -d $'%pre%' line < <(find "$dir" -maxdepth 1 -printf '%T@ %p%pre%' 2>/dev/null | sort -z -n)
file="${line#* }"
ls -lLd "$file"
' line < <(find . -maxdepth 1 -type d -printf '%T@ %p%pre%' \ 2>/dev/null | sort -z -n) file="${line#* }" # do something with $file here
' line < <(find "$dir" -maxdepth 1 -printf '%T@ %p%pre%' 2>/dev/null | sort -z -n) file="${line#* }" ls -lLd "$file"
' line < <(find . -maxdepth 1 -type d -printf '%T@ %p%pre%' \ 2>/dev/null | sort -z -n) file="${line#* }" # do something with $file here

O que tudo isso está fazendo?

Primeiro, os comandos find localizam todos os diretórios no diretório atual ( . ), mas não nos subdiretórios do diretório atual ( -maxdepth 1 ), depois imprimem:

  • Um registro de data e hora
  • Um espaço
  • O caminho relativo para o arquivo
  • Um caractere NULL

O registro de data e hora é importante. O %T@ do especificador de -printf divide em T , o que indica "Hora da última modificação" do arquivo (mtime) e @ , que indica "Segundos desde 1970", incluindo os segundos fracionários.

O espaço é meramente um delimitador arbitrário. O caminho completo para o arquivo é para que possamos nos referir a ele mais tarde, e o caractere NULL é um terminador porque é um caractere ilegal em um nome de arquivo e, portanto, nos informa com certeza que chegamos ao final do caminho para o arquivo. arquivo.

Eu incluí o 2>/dev/null para que os arquivos que o usuário não tem permissão para acessar sejam excluídos, mas as mensagens de erro sobre eles serem excluídos sejam suprimidas.

O resultado do comando find é uma lista de todos os diretórios no diretório atual. A lista é canalizada para sort , que é instruído para:

  • -z Trate NULL como o caractere terminador de linha em vez de nova linha.
  • -n Ordenar numericamente

Como os segundos desde 1970 sempre sobem, queremos o arquivo cujo registro de data e hora seja o menor número. O primeiro resultado de sort será a linha que contém o menor registro de data e hora numerado. Tudo o que resta é extrair o nome do arquivo.

Os resultados do find , sort pipeline são transmitidos por meio de substituição de processos para read , onde é lido como se fosse um arquivo em stdin.

No contexto de read , definimos a variável IFS como nada, o que significa que o espaço em branco não será interpretado de forma inadequada como um delimitador. read é informado -r , que desativa a expansão de escape, e -d $'find' , que torna o delimitador de fim de linha NULL, correspondendo à saída de nosso sort , line pipeline.

O primeiro bloco de dados, que representa o caminho do diretório mais antigo precedido por seu registro de data e hora e espaço, é lido na variável #* . Em seguida, a substituição de parâmetros é usada com a expressão $file , que simplesmente substitui todos os caracteres do início da string até o primeiro espaço, incluindo o espaço, sem nada. Isso remove o registro de data e hora da modificação, deixando apenas o caminho completo para o arquivo.

Neste ponto, o nome do arquivo é armazenado em rm -rf "$file" e você pode fazer o que quiser com ele, incluindo ls -t .

Não existe uma maneira mais simples?

Não. Formas mais simples são bugs.

Se você usar tail e pipe para rm $(anything) , você quebrará arquivos com novas linhas nos nomes dos arquivos. Se você rm "$(anything)" , os arquivos com espaço em branco no nome causarão a quebra. Se você %code% , os arquivos com novas linhas no nome causarão quebra.

Talvez, em casos específicos, você tenha certeza de que uma maneira mais simples é suficiente, mas nunca deve escrever suposições como essa em scripts, se puder evitar fazê-lo.

Editar

%pre%

Uma solução mais completa para o problema, pois ele verifica primeiro a contagem de diretórios.

    
por 12.01.2012 / 14:01
1

você pode usar algo parecido com o seguinte:

#!/bin/sh    
keep=3

while [ 'ls -1 | wc -l' -gt $keep ]; do
    oldest='ls -c1 | head -1'
    echo "remove $oldest"
    rm -rf $oldest
done

pode ser melhor usar find . -type d -maxdepth 1 em vez de ls . isso depende do esquema de nomenclatura usado para os diretórios. se eles forem naturalmente classificados corretamente pelo nome, você poderá usar find , sort e head ou tail para obter o diretório mais antigo / mais recente. a abordagem ls usa o atributo ctime para classificar.

    
por 12.01.2012 / 13:08
-1

Experimente:

rm $(ls -t1 | tail -n 1)
    
por 12.01.2012 / 12:56