Renomeia um subconjunto de arquivos em um diretório

1

Eu tenho centenas de milhares de arquivos em um diretório. Esses arquivos são nomeados da seguinte maneira:

left-00001.tiff
left-00002.tiff
...
left-99999.tiff
left-100000.tiff
...
left-245000.tiff

Gostaria de renomear os arquivos da seguinte forma:

left-000001.tiff
...
left-099999.tiff
...
left-245000.tiff

Encontrei uma solução elegante para esse problema aqui .

Esta solução implementa um script bash chamado zeropad.sh . o bash é codificado da seguinte forma:

#!/bin/bash
num='expr match "$1" '[^0-9]*\([0-9]\+\).*''
paddednum='printf "%06d" $num'
echo ${1/$num/$paddednum}

e pode ser aplicado iterativamente usando for loop da seguinte forma:

for i in *.tiff;do mv $i './zeropad.sh $i'; done

No entanto, essa solução leva muito tempo, pois faz muito trabalho desnecessário renomear todos os arquivos que já estão preenchidos adequadamente. %código%. Para meus propósitos, esta solução é muito lenta.

Eu tenho duas perguntas:

1- Como posso modificar o iterador para aplicar somente i.e. as %06d type numbers em arquivos que precisam ser preenchidos com zero?

2- Como posso usar o comando zeropad.sh em touch para gerar dados de teste? É crucial verificar se esse script funciona antes de aplicá-lo nos dados originais.

    
por kevinkayaks 05.09.2018 / 20:20

6 respostas

3

A maior parte do tempo gasto pelo seu loop provavelmente está chamando seu script zeropad.sh .

Em vez disso, faça tudo em um script:

#!/bin/bash

for filename in left-*.tiff; do
    if [[ "$filename" =~ ^left-0*([1-9]?[0-9]+)\.tiff$ ]]; then
        num=${BASH_REMATCH[1]}
        newname="left-$( printf '%06d' "$num" ).tiff"
        if [ "$filename" != "$newname" ] && [ ! -e "$newname" ]; then
            echo mv "$filename" "$newname"
        fi
    fi
done

Remova o echo depois de verificar se o script está fazendo a coisa correta.

    
por 05.09.2018 / 21:44
4

É assim que eu faço normalmente (manualmente no shell):

rename left- left-0 left-?.png    # for 0-9
rename left- left-0 left-??.png   # for 00-99
rename left- left-0 left-???.png  # for 000-999
# result left-0000.png - left-9999.png

Isso é fácil de fazer em uma sessão de shell interativa ... basta repetir o último comando com um% adicional? adicionado.

No entanto, com um grande número de arquivos, você acabará tendo uma lista muito longa de argumentos. E obviamente não é a escolha mais eficiente, pois acaba renomeando o mesmo arquivo várias vezes (left-1.png - > left-01.png - > left-001.png - > ...). / p>

Além disso, existem dois tipos de rename , um com expressões regulares perl e outro sem. Dependendo da distro, você acaba com rename.ul ou perl-rename ou outros nomes para eles. Basicamente, ele renderiza qualquer script usando o comando rename não portável, já que você nunca sabe o que esperar.

Estou usando a renomeação util-linux e sua pergunta é, na verdade, um dos exemplos deles, da página man:

EXAMPLES

Given the files foo1, ..., foo9, foo10, ..., foo278, the commands

    rename foo foo00 foo?
    rename foo foo0 foo??

will turn them into foo001, ..., foo009, foo010, ..., foo278.

Qual é o método mais eficiente (cada arquivo é renomeado apenas uma vez), mas você tem que descobrir a distribuição correta de 000 vs ??? ou você vai acabar com um resultado errado.

Para mim, o método ineficiente é o mais prático, em um shell interativo, ao lidar com um pequeno conjunto razoável de arquivos.

A vantagem de rename sobre a criação de scripts é que ela não precisa gerar um processo mv para cada arquivo ou, como no seu caso, um sub-script apenas para descobrir um nome de arquivo. Não está claro o que tem mais sobrecarga, o processo de desova ou renomeação repetida, e estou com preguiça de compará-lo.

Na verdade, a resposta que você vinculou já contém a solução "ótima" no final ... usando perl-rename:

rename 's/\d+/sprintf("%04d",$&)/e' *.png

Bem, pode-se argumentar sobre a expressão regular, mas o ponto é, é possível fazer tudo de uma só vez, sem mv desnecessário, ou desova de processos. Se você ainda precisar melhorar isso, escreva uma ferramenta que leia o conteúdo do diretório diretamente, em vez de usar o shell globbing (que ordena, que é lento) e execute a renomeação conforme necessário.

Talvez essa seja a resposta a que você ligou e talvez seja por isso que você está ganhando o negativo. ;)

    
por 05.09.2018 / 21:15
4

O que é caro é associar tantos processos e executar tantos comandos para cada arquivo.

com zsh :

zmodload zsh/files # make mv builtin to speed things up
autoload zmv
zmv -n '(*-)(<->)(.tiff)' '$1${(l:6::0:)2}$3'

(remova -n quando estiver satisfeito)

Isso é tudo com builtins, então não bifurca qualquer processo nem executa qualquer arquivo.

Ou com perl ' rename :

rename -n 's/\d+(?=\.tiff\z)/sprintf "%06d", $&/e' ./*[0-9].tiff
    
por 06.09.2018 / 00:22
2

Para a primeira parte, considere:

for i in left-?????.tiff left-????.tiff left-???.tiff left-??.tiff left-?.tiff ...'

Se isso gerar muitos arquivos, divida-os em seções:

for i in left-?????.tiff ...'

...

for i in left-????.tiff ...'

...

Os trabalhos acima usam o caractere ? glob para substituir qualquer caractere único sempre que ele aparecer. Aqui, solicitei especificamente 5, 4, 3, 2 e, em seguida, 1 dígitos após o primeiro left- .

Para a segunda parte, uma opção é:

dir=$(mktemp)
cd "$dir"
for i in $(seq 10); do touch $(printf 'left-%05d.tiff' $((RANDOM % 10000))); done

Ajuste o seq 10 para gerar mais ou menos nomes de arquivos. Ajuste o % 10000 para gerar números menores ou maiores. Observe que $ RANDOM do bash gera números entre 0 e 32.767.

    
por 05.09.2018 / 20:57
1

Eu adoro o one-liner do Perl:

ls left-*.tiff | perl -ne 'if(m/(\S+)-(\d+).tiff/){chomp;printf "mv $_ left-%06d.tiff\n", $2}' | bash

PS, certifique-se de checar novamente a saída antes de direcionar para bash . Apenas para estar seguro.

    
por 06.09.2018 / 00:59
1

Você pode renomear todos os arquivos em paralelo. Faça as seguintes alterações triviais no mesmo código lento que você forneceu em sua pergunta, da seguinte maneira:

cd data_folder  # cd the folder where you put the *.tiff files
for i in *.tiff;do 
{
mv $i './zeropad.sh $i';
}&

Isso renomeia todos os arquivos de uma só vez. Esteja ciente de que você deve ter recursos de memória suficientes em sua estação de trabalho antes de executar esse código dentro da pasta que contém os arquivos *.tiff . Nenhum recurso de memória suficiente pode causar falha de memória. Mas, dado que o processo está apenas renomeando arquivos, você deve estar bem!

Para levar em conta os recursos de memória em sua estação de trabalho. Salve o seguinte código em um arquivo chamado code , conceda permissões e execute-o:

mem=$(free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }' | grep Memory | awk '{print $3}' | tr -d "()%MB" | cut -d / -f 2  )

for i in *.tiff;do 
{
mv $i './zeropad.sh $i';
}&

if [ $mem -lt 100000 ] 
 then 
   if  (( "$i" % 75 == 0 ))
    then 
      sleep 4
   fi
 fi
if [ $mem -gt 100000 ] 
 then 
   if  (( "$i" % 300 == 0 ))
    then 
      sleep 3
   fi
 fi
done

Quando você executar code , ele verificará os recursos de memória em sua estação de trabalho usando a variável mem . Se a memória for menor que 100000MB , ela renomeará 75 files de uma só vez. Se os recursos de memória forem mais de 100000MB , ele renomeará até 300 files de uma só vez. No entanto, você pode ajustar todas as variáveis que quiser.

    
por 05.09.2018 / 22:14

Tags