Pregunta

Tengo un directorio llamado "imágenes" y relleno de aproximadamente un millón de imágenes. Sí.

Quiero escribir un comando shell para cambiar el nombre de todas esas imágenes en el formato siguiente:

ORIGINAL: filename.jpg
nuevo: /f/i/l/filename.jpg

¿Alguna sugerencia?

Gracias,
Dan

¿Fue útil?

Solución

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;

La parte ${i:0:1}/${i:1:1}/${i:2:1} probablemente podría ser una variable, o más corto o diferentes, pero el comando anterior hace el trabajo. Es probable que se enfrentan a problemas de rendimiento, pero si realmente desea utilizarlo, angosto el *.* a un menor número de opciones (a*.*, b*.* o lo que encaja)

Edit: añadido un $ antes i para mv, como se ha señalado por Dan

Otros consejos

Puede generar el nuevo nombre de archivo usando, por ejemplo, sed:

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

Por lo tanto, se puede hacer algo como esto (suponiendo que todos los directorios ya se crean):

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

o, si no se puede utilizar la sintaxis fiesta $(:

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

Sin embargo, teniendo en cuenta el número de archivos, es posible que sólo desee utilizar Perl ya que es una gran cantidad de procesos de sed y mv para desovar:

#!/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);
}

Eso Perl es sin duda limita sólo a personas del mismo sistema de archivos, aunque se puede utilizar File::Copy::move a conseguir alrededor de eso.

Puede hacerlo como un 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

Ni que decir tiene, es posible que tenga que preocuparse de los espacios y los archivos con nombres cortos.

Sugiero un script en Python corto. La mayoría de las herramientas de la shell se resisten a que gran parte de entrada (aunque xargs pueden hacer el truco). Se actualizará con el ejemplo en un 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)

Cualquiera de las soluciones propuestas que utilizan una sintaxis de comodín en la cáscara fallará probablemente debido al gran número de archivos que tienes. De las soluciones propuestas actuales, el Perl es probablemente el mejor.

Sin embargo, se puede adaptar fácilmente cualquiera de los métodos de secuencias de comandos shell para hacer frente a cualquier número de archivos de este modo:

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

Me gustaría volver a utilizar Perl, pero si usted está limitado a sólo tener herramientas simples UNIX alrededor, entonces la combinación de una de las soluciones anteriores de concha con un bucle, como he demostrado debe llegar hasta allí. Va a ser lento, sin embargo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top