Localiza e substitui uma string em um lote de arquivos, mas não cria conflitos de nome de arquivo

0

Eu tenho uma árvore de diretórios com alguns milhares de arquivos coletados ao longo dos anos com nomes de arquivos aleatórios e inúteis.

Eu quero limpá-los, mantê-los relevantes, remover certas palavras, mas também não criar nomes de arquivo duplicados.

Por exemplo

WONDERBROS - PAWG Remy LaCroix Gets BREAKFAST! (pwg11717) - SOMENAME.mp4

Eu quero que esse seja renomeado como

WONDERBROS.PAWG.Remy.LaCroix.Gets.BREAKFAST!.(pwg11717).mp4

Então, alguns pontos são

  • substitua todos os espaços por .
  • remover SOMENAME sempre que ocorrer
  • Verifique se esse nome de arquivo ainda não existe antes de renomear e, se houver, anexe um número e continue tentando até que seja um nome de arquivo exclusivo para que nada seja sobrescrito

Acho que isso pode ser feito com um script de shell, mas se alguém tiver uma ferramenta para recomendar, eu também gostaria de tentar.

Eu tentei algo assim e apenas a primeira linha parece funcionar.

# find and replace spaces with .
find /home/matt/rename_test_tmp/ -depth -name "* *" -execdir rename 's/ /./g' "{
# find and replace somename with .
find /home/matt/rename_test_tmp/ -depth -name "somename" -execdir rename 's/ 
# find and replace SOMENAME
find /home/matt/rename_test_tmp/ -depth -name "SOMENAME" -execdir rename 's/ 
# find and replace Somename
find /home/matt/rename_test_tmp/ -depth -name "Somename" -execdir rename 's/ 

Eu acho que o meu problema é que eu ainda não entendi reg ex?

    
por Matt_the_invader 24.02.2018 / 21:48

3 respostas

0

Aqui está uma estrutura básica. Se algo der errado, o MAXTRYS interrompe a renomeação. Escolha um valor razoável.

#!/bin/bash
#
# Usage: 
#       numberedMove xy-file.txt TARGETDIR 
#
file="$1"
targetDir="$2"
MAXTRYS=666
#
# @TODO
# add tests for permissions, maybe handle symlinks etc.
#
test -f "$file" || {
    echo No such file "$file" or not an ordinary file
    exit 1;
}

test -d "$targetDir" || {
    echo No such directory "$targetDir"
    exit 2;
}

#
# append NUM+1 to filename, to create unique name
#
numbered () {
  fname="$1"
  num=$2
  if [[ $num -gt $MAXTRYS ]]
  then
    echo " giving up - max trys: $MAXTRYS "
    exit 3
  fi
  # echo "mv $fname → $targetDir/$fname$num"
  test -f "$targetDir/$fname$num" && numbered "$fname" $((num+1)) || mv "$file" "$targetDir/$fname$num"
}

#
# remove all SOMENAME and replace every blank with a dot
#
filtername () {
   fname="$1"
   fname=${fname//SOMENAME/}
   fname=${fname// /.}
   echo "$fname"
}
#
# filter filename, if unique: move, else move with number suffix
#
if [[ -e "$file" ]]
then
    newname=$(filtername "$file")
    if [[ ! -f "$targetDir/$newname" ]]
    then
        # echo "mv $file → $targetDir/$newname"
        mv "$file" "$targetDir/$newname"
    else
        # echo "mv $file → $targetDir/$newname NUMBERED"
        numbered "$newname" 1
    fi
fi

Em filtername , você pode adicionar mais regras e alterar as existentes.

Se isso fosse para minhas próprias necessidades, eu substituiria espaços em branco por '-', não '.', já que o ponto é um indicador bastante fraco (a.tar.bz2) para significado.

E eu iria recolher vários delimitadores para um, então em vez de:

    mv "ab cd - SOMENAME..mp3" → "./B/ab.cd.-...mp3" 
    mv "ab cd - SOMENAME..mp3" → "./B/ab-cd-.mp3" 

Eu manteria a extensão do arquivo e colocaria o número na frente da última extensão:

    mv "ab cd- SOMENAME..mp3"  → "./B/ab-cd-0.mp3" 
    mv "ab cd - SOMENAME..mp3" → "./B/ab-cd-1.mp3" 
    mv "ab cd -SOMENAME..mp3"  → "./B/ab-cd-2.mp3" 

já que muitos programas interpretam a extensão.

Eu só testei o programa com cerca de 10 arquivos, então faça um backup antes de usar e verifique, talvez pelo tamanho total do arquivo e contagem de arquivos, se a operação foi bem-sucedida.

Meus testes:

for f in a* ; do ./numberedMove.sh "$f" ./B ; done

Note que, ao colocar o programa no CWD, ele pode ser movido sozinho.

    
por 25.02.2018 / 00:04
0

Se você estiver usando o utilitário de renomeação de perl, tente algo assim:

find . -type f -execdir rename -n 's/\s*-\s*/./g; s/\s+/./g; s/somename//ig' {} +

Os caracteres \s*-\s* correspondem a - rodeados por zero ou mais caracteres de espaços em branco (ou seja, - com espaços em branco opcionais). \s+ corresponde a um ou mais caracteres de espaço em branco.

Você pode adicionar quantos comandos s/search/replace/ no script de renomeação que desejar. Note, no entanto, que você precisa ter cuidado com a ordem de execução - por exemplo, Se você quiser alterar "foo" para "bar" e "alimentar" para "beber", precisará de s/food/drink/ antes de s/foo/bar/ , porque o último alterará food para bard .

O comando acima é aplicado a TODOS os nomes de arquivos. Se alguns dos comandos s/search/replace/ não se aplicarem a um nome de arquivo específico, isso não é um erro - esse comando específico não é aplicado (mas outros ainda são). Mesmo assim, você pode precisar executar vários comandos find usando o predicado -name ou -iname se desejar que algumas renomeações sejam aplicadas apenas a determinados nomes de arquivos.

Você também pode usar a alternação se quiser alterar várias palavras para a mesma substituição. por exemplo,

s/(somename|someothername|thisname|thatname)//ig

que altera todos eles para a cadeia vazia (isto é, remove-os). O modificador /i não faz distinção entre maiúsculas e minúsculas.

A opção -n de rename é uma opção de execução a seco - mostrará apenas o que seria renomeado, sem renomear nada. Remova o -n do comando renomear quando você verificar que ele faz o que você quer e nada que você não queira. Se você quiser uma saída detalhada durante a renomeação, substitua a -n por -v .

de man rename :

-v, -verbose

Verbose: print names of files successfully renamed.

-n, -nono

No action: print names of files to be renamed, but don't rename.

Observação : O utilitário perl rename não renomeará um arquivo se o novo nome de arquivo já existir. Ele não tem capacidade interna para adicionar números a nomes de arquivos em caso de colisão, mas uma das melhores características dessa renomeação é que você não está restrito a operações simples como s/search/replace/ - você pode executar any perl code dentro de um script de renomeação e ele renomeará o nome do arquivo de origem para qualquer $_ alterado (operadores como s/// ou y// implicitamente modificar $_ se um nome de variável explícito não for usado. Não é novidade em perl , há uma boa explicação sobre o $_ em link .

Isso permite algo assim (não testado, mas pode funcionar)

find . -type f -execdir rename -n '
    s/\s*-\s*/./g; 
    s/\s+/./g;
    s/somename//ig;

    if (-f $_) {
      my $num='001';
      while (-f "$_.$num") {
        $num=sprintf('%03i',++$num);
      };
      $_ = "$_.$num";
    }' {} +

Isso deve adicionar um número preenchido com zero de 3 dígitos (ou seja, de 001 a 999 ) ao nome do arquivo, se ele já existir. Altere %03i para %02i no sprintf se desejar apenas dois dígitos ( 01 to 99 ). Ou, se você não espera que a numeração de arquivos duplicados exceda 9 , altere-o para apenas my $num=1 e, em seguida, apenas $num++ dentro do loop while sem sprintf() .

com um pouco mais de trabalho (dividindo o nome do arquivo em porções de "base" e "extensão"), a numeração pode ser inserida antes da extensão, em vez de ser anexada ao final.

Você pode descobrir qual versão de rename você tem com a opção -V . por exemplo. no meu sistema Debian rename é a renomeação perl, enquanto rename.ul é a renomeação util-linux:

$ rename -V
/usr/bin/rename using File::Rename version 0.20

$ rename.ul -V
rename.ul from util-linux 2.31.1
    
por 25.02.2018 / 02:08
0
find . -maxdepth 1 -type f > orginal_file

find . -maxdepth 1 -type f -exec sed -r "s/\s+//g"| sed "s/SOMENAME//g" >> /var/tmp/modified file

paste orginal_file /var/tmp/modified file >> combined_orginal_modified_column_wise

for i in 'cat /var/tmp/modified file'
do
if [[ -f $i ]]
then
echo "file exsist"
else
awk -v i="$i" '/i/{print "mv" " " $1 " " $2}'   combined_orginal_modified_column_wise
fi
done

for i in 'cat /var/tmp/modified file'
do
if [[ -f $i ]]
then
for j in {1..10}
do
elif [[ -f $i$j ]]
then
echo "$i$j exsists"
fi
done
else
awk -v i="$i" -v j="$j" '{print "mv" " " $1 " " ij}'  combined_orginal_modified_column_wise
fi


done
    
por 25.02.2018 / 13:50

Tags