Question

J'ai un répertoire appelé « images » rempli d'environ un million d'images. Yep.

Je veux écrire une commande shell pour renommer toutes ces images dans le format suivant:

d'origine: filename.jpg
nouveau: /f/i/l/filename.jpg

Toutes les suggestions?

Merci,
Dan

Était-ce utile?

La solution

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 partie ${i:0:1}/${i:1:1}/${i:2:1} pourrait probablement être une variable, ou plus ou moins différents, mais la commande passe au-dessus du travail. Vous aurez probablement face à des problèmes de performance, mais si vous voulez vraiment l'utiliser, ciblez *.* à moins d'options (a*.*, b*.* ou ce qui vous convient)

edit: ajouté un $ avant i pour mv, comme l'a noté Dan

Autres conseils

Vous pouvez générer le nouveau nom de fichier en utilisant, par exemple, sed:

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

Alors, vous pouvez faire quelque chose comme ceci (en supposant que tous les répertoires sont déjà créés):

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

ou, si vous ne pouvez pas utiliser la syntaxe bash $(:

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

Toutefois, compte tenu du nombre de fichiers, vous pouvez juste utiliser Perl comme c'est beaucoup de processus sed et mv pour se reproduire:

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

Ce perl est sûrement limitée à même système de fichiers, mais vous pouvez utiliser File::Copy::move pour contourner cette difficulté.

Vous pouvez le faire comme 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

Inutile de dire que vous pourriez avoir à vous soucier des espaces et des fichiers avec des noms courts.

Je suggère un court script python. La plupart des outils shell rechignent à cette entrée beaucoup (bien que xargs peut faire l'affaire). Mettra à jour l'exemple dans une seconde.

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

Toutes des solutions proposées qui utilisent une syntaxe générique dans le shell va probablement échouer en raison du grand nombre de fichiers que vous avez. Parmi les solutions actuelles proposées, le Perl un est probablement le meilleur.

Cependant, vous pouvez facilement adapter l'une des méthodes de script shell pour traiter un certain nombre de fichiers ainsi:

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

Je encore utiliser Perl, mais si vous êtes limité à seulement avoir des outils simples unix autour, puis en combinant l'une des solutions de coque au-dessus avec une boucle comme je vous ai montré devrait y arriver. Ce sera lent, cependant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top