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!