Wie kann ich Dateien basierend auf dem Anfangsbuchstaben ihres Dateinamens in AZ-Ordnern organisieren?

15

Ich suche nach einer Möglichkeit (vorzugsweise Terminal), über 1000 Schriften nach ihrem Anfangsbuchstaben zu sortieren.

Erstellen Sie im Allgemeinen Verzeichnisse A-Z, #und verschieben Sie die Schriftdateien dann in diese Verzeichnisse, basierend auf dem Dateinamen des ersten Zeichens. Schriftarten, die mit Zahlen [0-9] oder anderen Sonderzeichen beginnen, werden in das #Verzeichnis verschoben .

Parto
quelle
Möchten Sie, dass die Verzeichnisse erstellt werden, auch wenn keine Dateien mit diesem Buchstaben beginnen?
Arronical
@Arronical Nope. Nur wenn es Dateien gibt.
Parto
4
Ich hoffe, dieser Link hilft Ihnen stackoverflow.com/questions/1251938/…
Karthickeyan

Antworten:

13

Eine späte Python-Option:

#!/usr/bin/env python3
import os
import sys
import shutil

def path(dr, f): return os.path.join(dr, f)

dr = sys.argv[1]
for f in os.listdir(dr):
    fsrc = path(dr, f)
    if os.path.isfile(fsrc):
        s = f[0]; target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
        if not os.path.exists(target):
            os.mkdir(target)
        shutil.move(fsrc, path(target, f))

Wie benutzt man

  1. Kopieren Sie das Skript in eine leere Datei und speichern Sie es unter move_files.py
  2. Führen Sie es mit dem Verzeichnis als Argument aus:

    python3 /path/to/move_files.py /path/to/files
    

Das Skript erstellt nur dann das (Unter-) Verzeichnis (-ies) (in Großbuchstaben), wenn es tatsächlich benötigt wird

Erläuterung

Das Drehbuch:

  • listet die Dateien auf, erhält das erste Zeichen (definiert den Quellpfad):

    for f in os.listdir(dr):
        s = f[0]; fsrc = path(dr, f)
  • prüft, ob der Gegenstand eine Datei ist:

    if os.path.isfile(fsrc):
  • Definiert den Zielordner, ob das erste Zeichen Alpha ist oder nicht:

    target = path(dr, s.upper()) if s.isalpha() else path(dr, "#")
  • prüft, ob der Ordner bereits existiert oder nicht, erstellt ihn, wenn nicht:

    if not os.path.exists(target):
        os.mkdir(target)
  • verschiebt das Objekt in den entsprechenden Ordner:

    shutil.move(fsrc, path(target, f))
Jacob Vlijm
quelle
Hey Jacob. Gibt es eine Prüfung auf Großbuchstaben?
Parto
@Parto Auf jeden Fall! Inwiefern brauchst du das? (Ich kann 3,5 Stunden Unterricht einchecken :)
Jacob Vlijm
Das hat es tatsächlich getan. Perfekte Umsetzung.
Parto
Nach Überlegung habe ich mich aus mehreren Gründen für diese Antwort entschieden: 1). Die Erklärung, was los ist. 2). Die Antwort lief beim ersten Versuch richtig
Parto
11

Code-golfed und doch lesbar mit nur zwei Befehlen und zwei regulären Ausdrücken:

mkdir -p '#' {a..z}
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|' [[:alnum:]]?*

Wenn Sie eine große Anzahl von Dateien verschieben müssen, zu viele, um in die Prozessargumentliste zu passen (ja, es gibt ein Limit und es können nur einige Kilobyte sein), können Sie die Dateiliste mit einem anderen Befehl generieren und diese an weiterleiten prename, z.B:

find -mindepth 1 -maxdepth 1 -name '[[:alnum:]]?*' -printf '%f\n' |
prename -n 's|^[[:alpha:]]|\l$&/$&|; s|^[0-9]|#/$&|'

Dies hat den zusätzlichen Vorteil, dass der Literaldateiname nicht verschoben wird, [[:alnum:]]?*wenn keine Dateien mit dem Glob-Muster übereinstimmen. finderlaubt auch viel mehr Übereinstimmungskriterien als Shell-Globbing. Eine Alternative besteht darin, die nullglobShell-Option festzulegen und den Standardeingabestream von zu schließen prename. 1

Entfernen Sie in beiden Fällen den -nSchalter, um die Dateien tatsächlich zu verschieben, und zeigen Sie nicht nur, wie sie verschoben würden.

Nachtrag: Sie können die leeren Verzeichnisse wieder entfernen mit:

rmdir --ignore-fail-on-non-empty '#' {a..z}

1 shopt -s nullglob; prename ... <&-

David Foerster
quelle
8

Wenn Sie nichts dagegen haben, zsh, eine Funktion und ein paar zmvBefehle:

mmv() {echo mkdir -p "${2%/*}/"; echo mv -- "$1" "$2";}
autoload -U zmv
zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'

Die mmvFunktion erstellt das Verzeichnis und verschiebt die Datei. zmvbietet dann Mustererkennung und -ersetzung. Verschieben Sie zuerst Dateinamen, die mit einem Alphabet beginnen, und dann alles andere:

$ zmv -P mmv '([a-zA-Z])(*.ttf)' '${(UC)1}/$1$2'
mkdir -p A/
mv -- abcd.ttf A/abcd.ttf
mkdir -p A/
mv -- ABCD.ttf A/ABCD.ttf
$ zmv -P mmv '([!a-zA-Z])(*.ttf)' '#/$1$2'
mkdir -p #/
mv -- 123.ttf #/123.ttf
mkdir -p #/
mv -- 七.ttf #/七.ttf

Führen Sie erneut ohne die echoin mmv‚s - Definition , um tatsächlich die Bewegung auszuführen.

muru
quelle
8

Ich habe keine gute Möglichkeit gefunden, die Verzeichnisnamen in Großbuchstaben zu schreiben (oder die Dateien in Großbuchstaben zu verschieben), obwohl Sie dies später mit rename...

mkdir {a..z} \#; for i in {a..z}; do for f in "$i"*; do if [[ -f "$f" ]]; then echo mv -v -- "$f" "$i"; fi; done; done; for g in [![:alpha:]]*; do if [[ -f "$g" ]]; then echo mv -v -- "$g" \#; fi; done

oder besser lesbar:

mkdir {a..z} \#; 
for i in {a..z}; do 
  for f in "$i"*; do
    if [[ -f "$f" ]]; then 
      echo mv -v -- "$f" "$i"; 
    fi 
  done
done
for g in [![:alpha:]]*; do 
  if [[ -f "$g" ]]; then 
    echo mv -v -- "$g" \#
  fi
done

Entfernen Sie echonach dem Testen, um die Dateien tatsächlich zu verschieben

Und dann

rename -n 'y/[a-z]/[A-Z]/' *

entfernen, -nwenn es nach dem Testen gut aussieht, und erneut ausführen.

Zanna
quelle
2
Sie können verwenden if [[ -d "${i^}" ]], um das variable iKapital und mkdir {A..Z}am Anfang zu machen.
Arronical
Lassen Sie mich das versuchen und sehen
Parto
@Arronical danke! Ich werde es jedoch verlassen, da Sie es auf Ihre eigene Weise gepostet haben
Zanna
@Zanna Mir gefällt, dass wir aus verschiedenen Richtungen darauf gekommen sind, indem wir die Buchstaben durchgearbeitet und als Suchkriterien verwendet haben, die mir noch nie in den Sinn gekommen sind. Ich bin sicher, es gibt eine clevere und schnelle Lösung mit find, aber ich kann es nicht fassen!
Arronical
Hey Zanna, das hat die Schriften, die mit einem Großbuchstaben beginnen, nicht verschoben. Ansonsten hat gut funktioniert.
Parto
7

Die folgenden Befehle in dem Verzeichnis, das die Schriftarten enthält, sollten funktionieren. Wenn Sie sie von außerhalb des Schriftarten-Speicherverzeichnisses verwenden möchten, ändern Sie sie for f in ./*in for f in /directory/containing/fonts/*. Dies ist eine sehr Shell-basierte Methode, also ziemlich langsam und auch nicht-rekursiv. Dadurch werden nur Verzeichnisse erstellt, wenn Dateien vorhanden sind, die mit dem entsprechenden Zeichen beginnen.

target=/directory/to/store/alphabet/dirs
mkdir "$target"
for f in ./* ; do 
  if [[ -f "$f" ]]; then 
    i=${f##*/}
    i=${i:0:1}
    dir=${i^}
    if [[ $dir != [A-Z] ]]; then 
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
    else
      mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir"
    fi
  fi
done

Als Einzeiler wieder aus dem Font-Speicher-Verzeichnis heraus:

target=/directory/to/store/alphabet/dirs; mkdir "$target" && for f in ./* ; do if [[ -f "$f" ]]; then i=${f##*/}; i=${i:0:1} ; dir=${i^} ; if [[ $dir != [A-Z] ]]; then mkdir -p "${target}/#" && mv "$f" "${target}/#"; else mkdir -p "${target}/$dir" && mv "$f" "${target}/$dir" ; fi ; fi ; done

Eine Methode, die find mit einer ähnlichen Zeichenfolgenmanipulation verwendet und eine Bash-Parameter-Erweiterung verwendet, die rekursiv ist und etwas schneller als die reine Shell-Version sein sollte:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs ; mkdir -p "$target"; f="{}" ; i="${f##*/}"; i="${i:0:1}"; i=${i^}; if [[ $i = [[:alpha:]] ]]; then mkdir -p "${target}/$i" && mv "$f" "${target}/$i"; else mkdir -p "${target}/#" && mv "$f" "${target}/#"; fi' \;

Oder besser lesbar:

find . -type f -exec bash -c 'target=/directory/to/store/alphabet/dirs 
   mkdir -p "$target"
   f="{}"
   i="${f##*/}"
   i="${i:0:1}"
   i=${i^}
   if [[ $i = [[:alpha:]] ]]; then 
      mkdir -p "${target}/$i" && mv "$f" "${target}/$i"
   else
      mkdir -p "${target}/#" && mv "$f" "${target}/#"
   fi' \;
Arronisch
quelle
Dieser hat auch funktioniert. Auch für Groß- und Kleinbuchstaben.
Parto
5

Ordnen Sie jeden Dateinamen einem Verzeichnisnamen zu, indem Sie trdann mkdirund verwenden mv:

find /src/dir -type f -print0 |
xargs -0 -I{} bash -c \
  'dir=/dest/$(basename "{}" | cut -c1 | tr -C "a-zA-Z\n" "#" | tr "a-z "A-Z"); mkdir -p $dir; mv "{}" $dir'
xn.
quelle
Ich mag das wirklich, das entspricht genau dem, was ich mit meinem Wunsch, find zu verwenden, begriffen habe. Gibt es eine Möglichkeit, nur Verzeichnisnamen in Großbuchstaben zu erstellen und Dateinamen in Kleinbuchstaben zu verschieben?
Arronical
1
Ich fügte ein anderes hinzu tr, um es in Großbuchstaben umzuwandeln.
xn.
Warum den Umweg xargseinfach bashnochmal durch telefonieren ? Wäre es nicht einfacher und viel lesbarer, die Ausgabe von findin eine while-Schleife zu leiten und readdort Datensatz für Datensatz abzuspielen?
David Foerster
Gute Frage, @DavidFoerster. Ich schätze, dass ich gegen Schleifen in Einzeilern bin und einen funktionaleren Ansatz bevorzuge. Aber ein Bash-Skript in einem String ist auch nicht sehr elegant, daher würde ich sagen, dass die whileLoop-Version ( bit.ly/2j2mhyb ) vielleicht besser ist.
xn.
Übrigens können Sie die lästige {}Ersetzung vermeiden, indem Sie xargsdas Argument anhängen lassen und dann $1im Shell-Skript nachschlagen, z xargs -0 -n1 -- bash -c 'dir=/dest/$(basename "$1" | ...); ...; mv "$1" "$dir"' _. (Beachten Sie das Finale _!)
David Foerster