Pergunta

Eu tenho um diretório chamado "imagens" cheio de cerca de um milhão de imagens. Sim.

Quero escrever um comando Shell para renomear todas essas imagens no seguinte formato:

original: filename.jpg
novo: /f/i/l/filename.jpg

Alguma sugestão?

Obrigado,
Dan

Foi útil?

Solução

for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done;

o ${i:0:1}/${i:1:1}/${i:2:1} A parte provavelmente poderia ser uma variável, ou mais curta ou diferente, mas o comando acima faz o trabalho. Você provavelmente enfrentará problemas de desempenho, mas se realmente quiser usá -lo, restrinja o *.* para menos opções (a*.*, b*.* ou o que se encaixa em você)

editar: adicionado um $ antes da i por mv, como observado por Dan

Outras dicas

Você pode gerar o novo nome do arquivo usando, por exemplo, sed:

$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'
t/e/s/test.jpg

Então, você pode fazer algo assim (assumindo que todos os diretórios já estejam criados):

for f in *; do
   mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')"
done

Ou, se você não pode usar a festa $( sintaxe:

for f in *; do
   mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`"
done

No entanto, considerando o número de arquivos, você pode apenas usar o Perl, pois são muitos processos Sed e MV para gerar:

#!/usr/bin/perl -w
use strict;

# warning: untested
opendir DIR, "." or die "opendir: $!";
my @files = readdir(DIR); # can't change dir while reading: read in advance
closedir DIR;
foreach my $f (@files) {
    (my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/;
    -e $new_name and die "$new_name already exists";
    rename($f, $new_name);
}

Que o Perl é certamente limitado apenas ao mesmo sistema de arquivos, embora você possa usar File::Copy::move para contornar isso.

Você pode fazer isso como um script bash:

#!/bin/bash

base=base

mkdir -p $base/shorts

for n in *
do
    if [ ${#n} -lt 3 ]
    then
        mv $n $base/shorts
    else
        dir=$base/${n:0:1}/${n:1:1}/${n:2:1}
        mkdir -p $dir
        mv $n $dir
    fi
done

Escusado será dizer que você pode precisar se preocupar com espaços e os arquivos com nomes curtos.

Eu sugiro um script python curto. A maioria das ferramentas de concha se recusará a tantas entradas (embora os Xargs possam fazer o truque). Atualizará com o exemplo em um segundo.

#!/usr/bin/python
import os, shutil

src_dir = '/src/dir'
dest_dir = '/dest/dir'

for fn in os.listdir(src_dir):
  os.makedirs(dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/')
  shutil.copyfile(src_dir+'/'+fn, dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/'+fn)

Qualquer uma das soluções propostas que usam uma sintaxe curinga no shell provavelmente falharão devido ao grande número de arquivos que você possui. Das soluções propostas atuais, o Perl é provavelmente o melhor.

No entanto, você pode adaptar facilmente qualquer um dos métodos de script da Shell para lidar com qualquer número de arquivos assim:

ls -1 | \
while read filename
do
  # insert the loop body of your preference here, operating on "filename"
done

Eu ainda usaria o Perl, mas se você estiver limitado a ter apenas ferramentas simples do Unix, combinando uma das soluções de shell acima com um loop como eu mostrei que deveria levá -lo até lá. Vai ficar lento, no entanto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top