6
votes

Linux: déplacez 1 million de fichiers en dossiers créés à base de préfixes

J'ai un répertoire appelé "images" rempli d'environ un million d'images. Oui.

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

Original: FileName.jpg
Nouveau: /f/i/l/filename.jpg

Toute suggestion?

merci,
Dan


0 commentaires

5 Réponses :


2
votes

Vous pouvez générer le nouveau nom de fichier à l'aide, par exemple, SED:

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


1 commentaires

Oh, je remarque une chose que les tests auraient trouvé: il faut un test "Est-ce un fichier?" Donc, cela ne déplace pas les répertoires. Assez facile à réparer (par exemple, -f $ f ou suivant; en haut de la boucle de Perl foreach, similaire dans la boucle de coquille)



1
votes

Je suggère un bref script Python. La plupart des outils Shellberont à majorité à cette contribution (bien que Xargs puissent faire l'affaire). Mettra à jour avec 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)


1 commentaires

Merci, ressemble à une solution merveilleuse. J'ai besoin d'attendre que les fichiers se transfèrent sur mon nouveau serveur avant de pouvoir l'essayer (ETA 50 heures lol)



7
votes
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;
The ${i:0:1}/${i:1:1}/${i:2:1} part could probably be a variable, or shorter or different, but the command above gets the job done. You'll probably face performance issues but if you really want to use it, narrow the *.* to fewer options (a*.*, b*.* or what fits you)edit: added a $ before i for mv, as noted by Dan

5 commentaires

FYI, le $ {i: 0: 1} Syntaxe est un ism Bash-ism, qui est probablement OK sur Linux, mais juste au cas où ...


S'il y a quelques répertoires dans le dossier, cette boucle les inclurea-t-elle aussi?


Besoin d'une correction: car je dans . ; do mkdir -p $ {i: 0: 1} / $ {i: 1: 1} / $ {i: 2: 1} /; mv $ i $ {i: 0: 1} / $ {i: 1: 1} / $ {i: 2: 1} /; terminé;


Seuls les répertoires avec des points en eux!


Dans mon cas, j'ai besoin de créer et de déplacer des fichiers vers un répertoire portant les sept premiers caractères du nom de fichier. Quelque chose comme FOO01, FOO02, FOO03 à la répertoire FOO; et BAR01, BAR02 à la barre d'annuaire. J'ai remplacé $ {i: 0: 1} / $ {i: 1: 1} / $ {i: 2: 1} / pour $ {i: 0: 7} / < / code> et cela a fonctionné comme un charme.



2
votes

Vous pouvez le faire sous forme de 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


0 commentaires

0
votes

L'une des solutions proposées qui utilisent une syntaxe Wildcard dans la coquille échouera probablement en raison du nombre de fichiers dont vous disposez. Parmi les solutions proposées actuelles, le Perl One est probablement le meilleur.

Cependant, vous pouvez facilement adapter les méthodes de script shell pour traiter n'importe quel nombre de fichiers ainsi: P>

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


4 commentaires

La syntaxe Wildcard devrait être bien, sa coquille intégrée et sa non-transmission de la ligne de commande à un programme exprès (sinon, sûrement, la ligne de commande serait trop longue). Pour i dans SEQ 1 1000000 fonctionne, par exemple.


Je viens de tester: utiliser pour f in * fonctionne parfaitement avec 1 000 000 fichiers. Lent, mais ça marche.


Merci pour votre commentaire, il était utile que je suis très nouveau dans les scripts shell


@derobert: Merci d'avoir testé cela et confirmant que fait travaille. Ceci est apparemment un cas où les leçons tirées des vieux jours ne sont pas nécessairement vraies. Bash apparemment amélioré cet aspect. Je sais pour un fait qu'il a échoué de différentes manières sous la coquille de Bourne, mais qui était de retour à la fin des années 80 / début des années 90 lorsque j'ai commis une erreur lors de la rédaction d'un script pour effectuer une maintenance sur les annuaires NetNews.