Auto increment filename

7

Eu preciso coletar alguns arquivos duplicados e evitar colisões de nomes. O problema é que essa coleção de arquivos pode ser adicionada por outra execução do meu script antes que os arquivos sejam limpos e apenas queira continuar incrementando o número.

Eu decidi por um loop simples até incrementar o número assim.

until [[ ! -f ${PWD}/DUPES/${num}-$1 ]]; do
num=$((num +1))
done
mv --no-clobber $1 ${PWD}/DUPES/${num}-$1

Eu não consigo imaginar arquivos acima de 1k em um exemplo extremo, então minhas perguntas são ...

Esta é uma maneira terrivelmente ineficiente para conseguir isso? Devo analisar os arquivos existentes para obter o maior número inicial para começar a incrementar a partir daí, ou isso é ok para o exemplo extremo que eu apresento? Ou existe uma maneira melhor de todos juntos?

    
por akovia 16.12.2015 / 21:28

3 respostas

5

Você pode gostar:

set -C; num=0                                  ### set -o noclobber; init $num
[ -e "$1" ] &&                                 ### this needs to be true
until 2>&3 >"./DUPES/$((num+=1))-$1" &&        ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do :; done 3>/dev/null                         ### do nothing

Você garantiria de imediato que várias instâncias não podem garantir o mesmo nome para nenhum arquivo e incrementar sua variável.

O /dev/null < stderr apenas descarta a reclamação do shell sobre um arquivo existente quando ele tenta fazer a saída truncate / redirect e encontra um alvo existente. Enquanto o noclobber estiver ativado, ele não sobrescreverá outro arquivo - somente open() será novo, a menos que você use >| . E assim você não precisa de sua reclamação porque o objetivo é incrementar os arquivos existentes até que um nome inexistente seja encontrado.

Em relação ao aspecto do desempenho - seria melhor se você não começasse no zero. Ou, se você tentou compensar a diferença. Eu acho que o acima pode ser melhorado um pouco como:

set -C; num=0                                  ### set -o noclobber; init $num
until 2>&3 >"./DUPES/$((num+=1))-$1"  &&       ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do    [ -e  "./DUPES/$((num*2))-$1" ] &&       ### halve fail distance
      num=$((num*2))                           ### up to a point of course
done  3>/dev/null                              ### done

... mas até 1000 você provavelmente não precisa se preocupar com isso terrivelmente. Eu tenho 65k sobre nomes aleatórios em alguns segundos.

A propósito, você pode pensar que poderia:

>"./DUPES/$((num+=1))-$1" mv -- "$1" "./DUPES/$num-$1"

... mas não funciona em bash shell.

num=0; echo >"/tmp/$((num+=1))" >&2 "$num"; echo "$num" /tmp/[01]
0
1 /tmp/1

Por alguma razão bash faz a atribuição em algum outro contexto para redirecionamentos - e assim as expansões acontecem em uma ordem estranha. Portanto, você precisa de um comando simples separado para expandir o valor correto de $num conforme eu chegar aqui com && . Caso contrário, porém:

num=0; echo "$((num+=1))" "$num"
1 1
    
por 16.12.2015 / 21:50
3

GNU mv tem uma opção --backup que pode ser útil. Os seguintes interação mostra enxada os arquivos estão recebendo renomea quando o alvo existe:

$ touch a b c o
$ mv --backup=numbered --verbose a o
'a' -> 'o' (backup: 'o.~1~')
$ mv --backup=numbered --verbose b o
'b' -> 'o' (backup: 'o.~2~')
$ mv --backup=numbered --verbose c o
'c' -> 'o' (backup: 'o.~3~')

Neste exemplo, o arquivo original o foi renomeado para o.~1~ , a a o.~2~ , b a o.~3~ e c a o . Então isso não muda o nome da mesma maneira que o código que você postou, mas isso pode ser aceitável, dependendo de suas necessidades exatas.

    
por 16.12.2015 / 21:43
1

Considere duas instâncias de sua proposta em execução simultaneamente, tendo iniciado simultaneamente. Ambos descobrirão que (digamos) num = 4 está disponível e ambos tentarão usar o mesmo nome de alvo.

Eu não posso testar essa alternativa agora, mas algo assim pode ser suficiente

num=1 attempt=10
while ! mv --no-clobber "$1" "${PWD}/DUPES/${num}-$1" && [[ 0 -lt $attempt ]]
do
    num=$((num+1))
    attempt=$((attempt-1))
done
[[ 0 -eq $attempt ]] && echo "ERROR: Too many attempts to handle $1" >&2
    
por 16.12.2015 / 21:42