Servidor Ubuntu: Apagar primeira pasta no diretório

1

Como posso pegar a primeira subpasta em um diretório e excluí-la?

Eu encontrei um script para monitorar o espaço livre em disco.

Ele envia um e-mail de alerta se o espaço estiver baixo, mas eu também quero excluir algumas coisas desnecessárias.

Eu tenho uma pasta de backup onde eu salvo backups diários e mensais. Eu quero apagar a primeira pasta, pois esta é sempre a mais antiga, mas não sei o nome do backup mais antigo.

Minhas pastas sem Jan-May e Dec:

06-01
07-01
08-01
09-01
10-01
11-01
Friday
Monday
Saturday
Sunday
Thursday
Tuesday
Wednesday

Como posso excluir a primeira pasta "06-01" sem saber o nome dela?

    
por Martin 02.11.2013 / 15:51

1 resposta

3

Se você quiser encontrar a primeira subpasta em relação à ordem alfanumérica (que, dependendo das configurações locais, tenha cuidado), você pode usar este pequeno snippet Bash:

shopt -s nullglob
shopt -u dotglob
a=( */ )
echo "${a[0]}"

isso apenas ecoará a primeira subpasta referente à classificação alfanumérica do Bash.

Se você quiser removê-lo, adicione alguns protetores:

shopt -s nullglob
shopt -u dotglob
a=( */ )
[[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"

Agora, não tenho certeza se usaria isso pessoalmente em um servidor de produção. Depende de como certeza eu sou o conteúdo do diretório (e como estou seguro pelos backups ).

Os shopt -s nullglob e shopt -u dotglob estão aqui para evitar algum erro bobo que poderia ocorrer se eles estivessem definidos de outra forma. Além disso, observe as citações e não as altere.

Oh, deixei um echo na frente do rm . Só remova quando tiver certeza de que isso funciona exatamente como esperado, depois de vários testes. Como um one-liner:

shopt -s nullglob; shopt -u dotglob; a=( */ ); [[ -d "${a[0]}" ]] && echo rm -rfv -- "${a[0]}"

Se você quiser especificar o caminho em que as subpastas estão, basta substituir a instrução a=( */ ) por:

a=( /path/to/folder/*/ )

certifique-se de colocar um caminho absoluto e terminar com */ (com uma barra final). Se você tiver espaços em seu caminho, use aspas da seguinte forma:

a=( '/path with/spaces to/folder/'*/ )

(observe como o */ está fora da parte citada e não há espaços após a cotação de fechamento e */ ).

Agora, se você realmente quiser determinar a subpasta mais antiga (com uma certa indeterminação se vários tiverem a mesma idade), você pode (estremecer) analisar a saída de ls ordenada pelo tempo na ordem inversa e pegue o primeiro e remova-o, como em:

ls -1tr --group-directories-first /path/to/folder | head -1 | xargs echo rm -rfv

mas isso falhará se não houver diretórios (você pode apagar o arquivo mais antigo) ou falhará se houver arquivos contendo espaços ou símbolos engraçados. Não faça isso.

Você poderia solicitar ls */ sem listar o conteúdo dos diretórios com a opção -d :

ls -1dtr path/to/folder/*/ | head -1 | xargs echo rm -rfv

mas isso falharia miseravelmente se houvesse espaços no nome. Não faça isso.

Você pode tentar corrigir isso usando a opção -Q para ls (ou seja, citar a saída):

ls -1dtQr path/to/folder/*/ | head -1 | xargs echo rm -rfv

mas isso falha se houver cotações ou novas linhas em um nome de arquivo. Não faça isso.

Então você tentaria xargs com a opção -0 :

ls -1dtQr path/to/folder/*/ | head -1 | xargs -0 echo rm -rfv

mas isso falha se houver novas linhas em um nome de arquivo. Não faça isso.

Então você poderia ... PARAR!

Se você realmente quer um método 100% à prova de bala, isso pode não ser o mais eficiente, mas isso servirá para você se a segurança for sua principal prioridade, aqui está uma possibilidade (no Bash):

shopt -s nullglob
shopt -u dotglob
where="/path with spaces is ok/to/folder"
for i in "$where"/*/; do
    if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
        echo rm -rfv -- "$i"
        break
    fi
done

Isso não é o mais eficiente, mas funciona muito bem: percorre todos os diretórios (de primeiro nível) i no diretório where e lança find para obter os diretórios (não ocultos) mais antigos que o diretório i e se encontrar algum não faz nada; se não encontrar nenhum, então quebramos echo rm e quebramos o loop. O efeito colateral é que o diretório que será excluído é o mais antigo e o primeiro com relação à ordem alfanumérica do Bash.

Você pode até criar um roteiro interessante (chame-o, por exemplo, rm_oldest_dir ):

#!/bin/bash

if [[ $1 = --serious ]]; then
    serious=()
    shift
else
    serious=("echo")
fi

if [[ -z $1 ]]; then
    echo >&2 "You must provide an argument"
    exit 1
fi

where=$1

if [[ ! -d $where ]]; then
    echo >&2 "\'$where' is not a directory"
    exit 1
fi

shopt -s nullglob
shopt -u dotglob
for i in "$where"/*/; do
    if ! find "$where" -mindepth 1 -maxdepth 1 \! -name '.*' -type d \! -newer "$i" \! -samefile "$i" -print | read; then
        "${serious[@]}" rm -rfv -- "$i"
        if ((${#serious[@]})); then
            echo "*** Nothing has been removed. Call with option --serious to really remove. ***"
        fi
        break
    fi
done

Em seguida, use como, por exemplo,

$ mkdir Test
$ cd Test
$ mkdir oldest; sleep 1; mkdir lots_of_dirs{1..10}
$ rm_oldest_dir .
rm -rfv -- ./oldest/
*** Nothing has been removed. Call with option --serious to really remove. ***
$ ls
lots_of_dirs1   lots_of_dirs3  lots_of_dirs6  lots_of_dirs9
lots_of_dirs10  lots_of_dirs4  lots_of_dirs7  oldest
lots_of_dirs2   lots_of_dirs5  lots_of_dirs8
$ rm_oldest_dir --serious .
removed directory: './oldest'
$ ls
lots_of_dirs1   lots_of_dirs2  lots_of_dirs4  lots_of_dirs6  lots_of_dirs8
lots_of_dirs10  lots_of_dirs3  lots_of_dirs5  lots_of_dirs7  lots_of_dirs9
$

Limpo.

Você poderia colocá-lo no seu crontab como

0 0 * * * rm_oldest_dir --serious "/path with spaces/to/where/you/want"

O único ataque contra esse script que eu posso imaginar é se houver muitas pastas: o globbing do Bash não é muito eficiente, e muitas pastas podem levar a um uso muito alto da CPU por um longo tempo e eventualmente terminar como um aborto . Um método mais envolvido seria um meio quicksort baseado em arquivo, mas como esse post já é muito longo, terminarei aqui.

Felicidades!

Eu não sei se a segunda parte desta resposta realmente tem seu lugar no SF. Se não, por favor, deixe-me saber nos comentários ou downvotar este post!

    
por 02.11.2013 / 16:07