Como renomear arquivos de backup, usando sua versão de backup como um sufixo para o nome do arquivo e tornando-o não mais oculto

Aug 17 2020

Tenho trabalhado com um grande número de arquivos de imagem. Parte disso foi mover todos os tipos de arquivo de imagem espalhados por mais de 1000 diretórios em um único diretamente. Havia muitas fotos com o mesmo nome exato, mas na verdade eram fotos diferentes. Usei o seguinte one-liner para fazer isso:

find . -type f -exec mv --backup=t '{}' /media/DATA-HDD/AllImages \;

Fiz assim para que qualquer imagem que tivesse o mesmo nome tivesse um arquivo de backup oculto feito, em vez de sobrescrito. Funcionou muito bem, mas agora tenho outro problema que preciso resolver.

Agora, é claro, tenho muitos tiles que são mais ou menos como o seguinte:

DSC_0616.NEF
DSC_0616.NEF.~1~
DSC_0616.NEF.~2~

O que estou procurando fazer é executar um comando (ou script) que renomeie qualquer um desses arquivos ocultos adicionando o número do backup como um sufixo ao nome do arquivo e remova o. ~ [Bu #] ~ para torná-los exclusivos nomes de arquivo e não oculto. Então, assim:

DSC_0616.NEF
DSC_0616_1.NEF
DSC_0616_2.NEF

Passei a maior parte de algumas horas tentando pesquisar para tentar fazer isso sozinho, mas realmente não consigo encontrar nada que possa me ajudar a chegar lá que esteja dentro do meu domínio de conhecimento sobre o assunto.

Respostas

1 Minty Aug 17 2020 at 09:57

Contanto que você tenha certeza de que tudo é nomeado de forma consistente conforme descrito acima, algumas expressões regulares podem realizar o trabalho por meio de um script de shell:

#!/bin/bash
# sets the file separator to be only newlines, in case files have spaces in them
IFS=$'\n' for file in $(find . -type f); do
        # parses just the number(s) between two tildes, and only at the end of the file
        number=$(echo $file | grep -Eo "~[0-9]+~$" | sed s/'~'/''/g) # if no match found, assume this is a "base" file that does not need to be renamed if [ "$number" == "" ]; then
                continue
        fi
        # parses the file name through "NEF", then deletes ".NEF"
        filename=$(echo $file |  grep -Eio "^.+\.NEF" | sed s/'\.NEF'/''/g )
        if [ "$filename" == "" ]; then continue fi mv -v $file $(echo "$filename"_"$number.NEF") # if anything went wrong, exit immediately if [ "$?" != "0" ]; then
                echo "Unable to move file $file"
                exit 1
        fi
done

Isso funcionará para descer pelos diretórios também, basta colocar o script e executá-lo com seu diretório de trabalho no topo da árvore de diretórios do seu projeto. Executando com arquivos de exemplo como você forneceu:

###@###:~/project$ find . -type f
./DSC_0616.NEF.~8~
./DSC_0616.NEF.~5~
./DSC_0616.NEF.~1~
./DSC_0616.NEF.~7~
./DSC_0616.NEF.~3~
./DSC_0616.NEF.~4~
./DSC_0616.NEF.~9~
./DSC_0616.NEF.~2~
./DSC_0616.NEF.~6~
./lower_dir/DSC_0616.NEF.~8~
./lower_dir/DSC_0616.NEF.~5~
./lower_dir/DSC_0616.NEF.~1~
./lower_dir/DSC_0616.NEF.~7~
./lower_dir/DSC_0616.NEF.~3~
./lower_dir/DSC_0616.NEF.~4~
./lower_dir/DSC_0616.NEF.~9~
./lower_dir/DSC_0616.NEF.~2~
./lower_dir/DSC_0616.NEF.~6~

depois de executar o script:

###@###:~/project$ find . -type f
./DSC_0616_1.NEF
./DSC_0616_3.NEF
./DSC_0616_7.NEF
./DSC_0616_5.NEF
./DSC_0616_2.NEF
./DSC_0616_9.NEF
./DSC_0616_6.NEF
./DSC_0616_8.NEF
./DSC_0616_4.NEF
./lower_dir/DSC_0616_1.NEF
./lower_dir/DSC_0616_3.NEF
./lower_dir/DSC_0616_7.NEF
./lower_dir/DSC_0616_5.NEF
./lower_dir/DSC_0616_2.NEF
./lower_dir/DSC_0616_9.NEF
./lower_dir/DSC_0616_6.NEF
./lower_dir/DSC_0616_8.NEF
./lower_dir/DSC_0616_4.NEF