2016-07-30 3 views
-2

Dies ist im Grunde ein Follow-up zu Linux: Move 1 million files into prefix-based created FoldersLinux: Update Verzeichnisstruktur für Millionen von Bildern, die bereits in Präfix-basierte sind Ordner

Die ursprüngliche Frage:

Ich möchte einen Shell-Befehl schreiben

original: filename.jpg neu: all diese Bilder in die folgende Format umbenennen /f/i/l/filename.jpg

Jetzt

, möchte ich all diese Dateien zu übernehmen und eine zusätzliche Ebene der Verzeichnisstruktur hinzufügen, zum Beispiel:

original: /f/i/l/filename.jpg neu: /f/i/l/e/filename.jpg

Ist dies möglich, mit Befehlszeile zu tun oder Bash?

+0

Mit welchem ​​Teil Ihrer ursprünglichen Frage haben Sie Probleme? Es ist im Wesentlichen das gleiche Konzept. –

+0

Schleife durch die vorhandene Verzeichnisstruktur, ohne die neu erstellten Ordner einzugeben – Dan

+0

Warum Schleife überhaupt? 'cd f/i/l; mkdir e; cp * e; rm * 'sollte tun, was Sie wollen ... (Sie sollten das letzte' rm' in 'echo' ändern, um es zuerst zu testen) ... –

Antwort

1

Eine Möglichkeit, es zu tun ist, einfach Schleife über alle Verzeichnisse, die Sie bereits haben, und in jedem Unterverzeichnis der untersten Ebene erstellen das neue Unterverzeichnis und verschieben Sie die Dateien:

for d in ?/?/?/; do (
    cd "$d" && 
    printf '%.4s\0' * | uniq -z | 
    xargs -0 bash -c 'for prefix do 
         s=${prefix:3:1} 
         mkdir -p "$s" && mv "$prefix"* "$s" 
        done' _ 
) done 

, die wahrscheinlich ein bisschen braucht Erläuterung.

Der Glob ?/?/?/ stimmt mit allen Verzeichnispfaden überein, die aus drei Einzelzeichen-Unterverzeichnissen bestehen. Da es mit einem / endet, ist alles, was es entspricht, ein Verzeichnis, so dass es keine Notwendigkeit gibt, zu testen.

(cd "$d" && ...;) 

... ausführt, nachdem es dem entsprechenden Unterverzeichnis wechseln mit cd. Wenn Sie diesen Block in () setzen, wird er in einer Subshell ausgeführt, was bedeutet, dass der Gültigkeitsbereich von cd auf den geklammerten Block beschränkt ist. Das ist einfacher und sicherer als cd .. am Ende zu setzen.

Wir haben dann zunächst die Unterverzeichnisse zu sammeln, indem Sie die einzigartigen ursprünglichen Strings der Dateien zu finden:

printf '%.4s\0' * | uniq -z | xargs -0 ... 

, dass die ersten vier Buchstaben jeden Dateinamen extrahiert, nul abbreche jeden, dann übergibt diese Liste an uniq um die Duplikate zu eliminieren, indem die -z Option zur Verfügung gestellt wird, da die Eingabe nullterminiert ist, und dann die Liste der eindeutigen Präfixe an xargs weitergibt, wiederum unter Verwendung von -0, um anzuzeigen, dass die Liste nullterminiert ist. xargs führt einen Befehl mit einer Liste von Argumenten aus und gibt den Befehl nur dann mehrfach aus, wenn dies erforderlich ist, um eine Überschreitung des Befehlszeilenlimits zu vermeiden. (Wir hätten wahrscheinlich die Verwendung von xargs vermeiden können, aber es kostet nicht viel und es ist viel sicherer.)

Der mit xargs aufgerufene Befehl ist bash; Wir verwenden die Option -c, um einen auszuführenden Befehl zu übergeben. Dieser Befehl iteriert seine Argumente mithilfe der Syntax for arg in. Jedes Argument ist ein eindeutiges Präfix. Wir extrahieren das vierte Zeichen aus dem Präfix, um das neue Unterverzeichnis zu erstellen, und dann mv alle Dateien, deren Namen mit dem Präfix im neu erstellten Verzeichnis beginnen.

Die _ am Ende des Aufrufs xargs wird an bash weitergegeben (wie bei allen anderen Argumenten); bash -c verwendet das erste Argument nach dem Befehl als $0-Argument für das Skript, das nicht Teil der Befehlszeilenargumente ist, die von der for arg in-Syntax durchlaufen werden. Die _ zu setzen bedeutet also, dass die Argumentenliste, die durch xargs erstellt wird, genau $1, $2, ... in der Ausführung des Bash-Befehls ist.

+0

Bloody brilliant, danke. Ich brauchte eine Weile, um die Unterstreichung zu verstehen. Ich bin ein wenig verwirrt darüber, wie genau die Argumente von xargs übernommen werden. Wie wird 'Präfix' zugewiesen? – Dan

+1

@dan: 'for var in' ist eine spezielle Form der 'for'-Anweisung, die über die Argumente des Befehls ($ 1, $ 2, ...) iteriert. 'xargs' nimmt eine Liste von Argumenten von seiner stdin und wendet sie auf den Befehl an, der als seine Argumente geliefert wird. Hier bedeutet "apply", dass am Ende des Befehls immer so viele Befehle wie möglich gesetzt werden, bis sie alle verwendet haben. – rici

+0

Ah, okay. Also wird der Unterstrich an den Inline-Befehl als $ 0 übergeben, der nicht von der for-Schleife durchlaufen wird. – Dan

0

Okay, so habe ich erstellt eine sehr grobe Lösung:

#!/bin/bash 

for file1 in *; do 
    if [[ -d "$file1" ]]; then 
     cd "$file1" 
     for file2 in *; do 
      if [[ -d "$file2" ]]; then 
       cd "$file2" 
       for file3 in *; do 
        if [[ -d "$file3" ]]; then 
         cd "$file3" 
         for file4 in *; do 
          if [[ -f "$file4" ]]; then 
           echo "mkdir -p ${file4:3:1}/; mv $file4 ${file4:3:1}/;" 
           mkdir -p ${file4:3:1}/; mv $file4 ${file4:3:1}/; 
          fi 
         done 
         cd .. 
        fi 
       done 
       cd .. 
      fi 
     done 
     cd .. 
    fi 
done 

ich soll warnen, dass dies nicht getestet ist, als meine eigentliche Struktur leicht verändert, aber ich wollte die Frage halten/Antwort im Einklang mit der ursprüngliche Frage zur Klarheit.

Das gesagt, ich bin sicher, dass eine viel elegantere Lösung existiert als diese.

+0

Das ist keine unangemessene Lösung, besonders wenn Sie es nur einmal machen wollen. Ich habe eine etwas kürzere und wahrscheinlich schnellere Version zur Verfügung gestellt, aber ich weiß nicht, ob Sie sie "eleganter" finden werden. – rici

Verwandte Themen