
Tenho trabalhado com um grande número de arquivos de imagem. Parte disso foi mover todos os tipos de arquivos de imagem que estavam espalhados por mais de 1.000 diretórios em um único diretório. Havia muitas fotos que tinham exatamente o mesmo nome, mas na verdade eram fotos diferentes. Usei a seguinte linha para fazer isso:
find . -type f -exec mv --backup=t '{}' /media/DATA-HDD/AllImages \;
Fiz dessa forma para que qualquer imagem que tivesse o mesmo nome recebesse um arquivo de backup oculto, em vez de sobrescrevê-lo. Funcionou muito bem, mas agora tenho outro problema que preciso resolver.
Agora, é claro, tenho muitos blocos que são mais ou menos assim:
DSC_0616.NEF
DSC_0616.NEF.~1~
DSC_0616.NEF.~2~
O que pretendo fazer é executar um comando (ou script) que renomeará qualquer um desses arquivos ocultos adicionando o número de backup como um sufixo ao nome do arquivo e removendo o .~[bu#]~ para torná-los únicos nomes de arquivos e não ocultos. 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 não consigo encontrar nada que possa me ajudar a chegar lá que esteja dentro do meu domínio de conhecimento sobre o assunto.
Responder1
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 também funcionará para descer pelos diretórios, 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
Responder2
Nos comentários, o OP solicitou uma solução que funcionasse para qualquer extensão (não apenas .NEF), e eu simplesmente tive o mesmo problema. :p
Aqui está um com Bashismos um pouco mais modernos do que a primeira resposta:
#!/usr/bin/env bash
# Renames the $1 directory's backup files (produced by `cp` or `mv` with the
# option `--backup=numbered`) such that "foo.png.~17~" becomes "foo_17.png"
function rename_backups()
{
for THING in "${1}"/*
do
if [[ -f "${THING}" &&
"${THING}" =~ ^.+\/+.+\..+~([0-9]+)~$ ]] # Take "foo.png.~17~".
then
# Get the "17".
local BACKUP_NUMBER=${BASH_REMATCH[1]}
# Remove the "~17~", leaving "foo.png".
local SANS_BACKUP_NUMBER="${THING%.*}"
# Get the filename, "foo", and the extension, "png".
local FILENAME="${SANS_BACKUP_NUMBER%.*}"
local EXT="${SANS_BACKUP_NUMBER##*.}"
# Rename to "foo_17.png".
local NEW_NAME="${FILENAME}_${BACKUP_NUMBER}.${EXT}"
while [[ -f "${NEW_NAME}" ]]
do
# If "foo_17.png" already exists for some reason, keep
# increasing the number until we find an unused one.
((++BACKUP_NUMBER))
NEW_NAME="${FILENAME}_${BACKUP_NUMBER}.${EXT}"
done
mv --no-clobber "${THING}" "${NEW_NAME}"
fi
done
}
rename_backups your/backup/directory/here/
A versão abaixo também suportasem extensãoarquivos:
#!/usr/bin/env bash
# Renames the $1 directory's backup files (produced by `cp` or `mv` with the
# option `--backup=numbered`) such that:
# - "foo.png.~17~" becomes "foo_17.png"
# - "bar.~17~" becomes "bar_17"
function rename_backups()
{
for THING in "${1}"/*
do
# We'll either have a file like "foo.png.~17~" or "bar.~17~".
if [[ -f "${THING}" && "${THING}" =~ ^.+~([0-9]+)~$ ]]
then
# Get the "17".
local BACKUP_NUMBER=${BASH_REMATCH[1]}
# Remove the "~17~", leaving, e.g., "foo.png".
local SANS_BACKUP_NUMBER="${THING%.*}"
# Get the filename, "foo", and the extension, "png".
local FILENAME="${SANS_BACKUP_NUMBER%.*}"
local EXT="${SANS_BACKUP_NUMBER##*.}"
if [[ "${FILENAME}" == "${SANS_BACKUP_NUMBER}" ]]
then
# This is a "bar.~17~" case, so rename to "bar_17".
local NEW_NAME="${FILENAME}_${BACKUP_NUMBER}"
while [[ -f "${NEW_NAME}" ]]
do
# If "bar_17" already exists for some reason, keep
# increasing the number until we find an unused one.
((++BACKUP_NUMBER))
NEW_NAME="${FILENAME}_${BACKUP_NUMBER}"
done
else
# This is a "foo.png.~17~" case, so rename to "foo_17.png".
local NEW_NAME="${FILENAME}_${BACKUP_NUMBER}.${EXT}"
while [[ -f "${NEW_NAME}" ]]
do
((++BACKUP_NUMBER))
NEW_NAME="${FILENAME}_${BACKUP_NUMBER}.${EXT}"
done
fi
mv --no-clobber "${THING}" "${NEW_NAME}"
fi
done
}
rename_backups your/backup/directory/here/