2009-08-07 5 views

Antwort

6
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; 

Der ${i:0:1}/${i:1:1}/${i:2:1} Teil wahrscheinlich könnte eine Variable oder kürzer oder verschieden sein, aber der Befehl wird über die ausgeführte Arbeit. Sie werden wahrscheinlich Performance-Probleme stellen, aber wenn Sie es wirklich verwenden möchten, verengen die *.* zu weniger Optionen (a*.*, b*.* oder was Ihnen passt)

edit: hat einen $ vor i für mv, wie von Dan bemerkt

+1

FYI, die '$ {i: 0: 1}' Syntax ein bash-ism, das ist wahrscheinlich OK auf Linux, aber nur für den Fall ... – derobert

+0

Wenn es einige Verzeichnisse im Ordner gibt, wird diese Schleife sie auch einschließen? – Dan

+0

Benötigt eine Korrektur: für 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} /; erledigt; – Dan

2

Sie können den neuen Dateinamen verwenden, zB sed erzeugen: Sie können wie folgt tun etwas

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

Also, (vorausgesetzt, dass alle Verzeichnisse sind bereits erstellt) :

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

oder, wenn Sie nicht die bash $( Syntax:

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

jedoch die Anzahl der Dateien unter Berücksichtigung haben, können Sie nur Perl verwenden, wie viel sed und mv Prozesse ist zum Laichen:

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

, dass Perl ist sicherlich begrenzt auf same-Dateisystem nur , obwohl Sie File::Copy::move verwenden können, um das zu umgehen.

+0

Dank für diese Lösung ist es ein interessanter Ansatz – Dan

+0

oh, ich eines feststellen, dass Tests gefunden hätten: „ist dies eine Datei“ Es gibt einen Test sein muß Es verschiebt also nicht die Verzeichnisse. Ziemlich einfach zu beheben (z. B. '-f $ f oder nächste;' an der Spitze der Perl-foreach-Schleife, ähnlich in der Shell-Schleife) – derobert

1

Ich schlage ein kurzes Python-Skript vor. Die meisten Shell-Tools werden bei dieser Eingabe scheitern (obwohl Xargs den Trick machen können). Wird mit Beispiel in einer Sekunde aktualisiert.

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

danke, sieht aus wie eine wunderbare Lösung. Ich muß warten, bis die Dateien auf einem anderen Rechner zu übertragen, bevor ich es ausprobieren kann (ETA 50 Stunden lol) – Dan

2

Sie können es als ein bash-Skript tun:

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

Unnötig zu sagen, Sie könnten über Räume und die Dateien mit kurzen Namen zu kümmern.

+0

sehr schöne Lösung, danke – Dan

0

Jede der vorgeschlagenen Lösungen, die eine Wildcard-Syntax in der Shell verwenden, wird wahrscheinlich scheitern aufgrund der schiere Anzahl der Dateien, die Sie haben. Von den derzeit vorgeschlagenen Lösungen ist die Perl-Version wahrscheinlich die beste.

Sie können jedoch leicht eine der Shell-Skript Methoden anpassen somit mit einer beliebigen Anzahl von Dateien umgehen:

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

ich noch Perl verwenden würde, aber wenn Sie auf nur einfache Unix-Tools mit herum, dann kombiniert man eine der oben genannten Shell-Lösungen mit einer Schleife, wie ich gezeigt habe, sollte Sie dorthin bringen. Es wird aber langsam sein.

+0

Wildcard-Syntax in Ordnung sein sollte, seine eine Schale eingebaut und es ist nicht auf der Kommandozeile zu einem Programm absichtlich übergeben wird (andernfalls sicher, die Befehlszeile wäre zu lang). zum Beispiel funktioniert i in 'seq 1 1000000'. – derobert

+0

Ich habe gerade getestet: die Verwendung von 'für f in *' funktioniert gut mit 1.000.000 Dateien. Langsam, aber es funktioniert. – derobert

+0

Dank für Ihren Kommentar, es war hilfreich, wie ich bin sehr neu Scripting, Shell – Dan