Rekursives Umbenennen von Dateien mithilfe einer Liste von Mustern und Ersetzungen

Rekursives Umbenennen von Dateien mithilfe einer Liste von Mustern und Ersetzungen

Ich habe folgende Dateistruktur:

  • Irgendein Verzeichnis
    • Irgendeine Datei.txt
    • Eine weitere Datei hier.log
    • Noch eine Datei.mp3
  • Ein anderes Verzeichnis
    • Mit einer anderen Datei.txt
  • Datei auf Stammebene.txt
  • Eine weitere Datei auf Stammebene.ext

Was ich jetzt tun möchte, ist, ein kleines Skript auszuführen, das eine andere Datei als Eingabe verwendet, die eine Art Muster/Ersetzungspaare enthält, um diese Dateien rekursiv entsprechend umzubenennen. So dass jedes „another“ (ohne Berücksichtigung der Groß-/Kleinschreibung) durch „foo“ oder jedes „some“ durch „bar“ ersetzt wird.

Ich habe schon vieles ausprobiert, indem ich über Dateien iterierte und die Eingabedatei einlas, aber nichts funktionierte so, wie es sollte, und schließlich habe ich es geschafft, versehentlich mein Testskript zu überschreiben. Aber es waren viele ls, while, sedoder mvim Einsatz.

Die beiden Dinge, die ich nicht selbst lösen konnte, waren der Umgang mit Leerzeichen in Dateinamen und der Nichtumgang mit Dateien, die bereits bei einer vorherigen Musterübereinstimmung umbenannt wurden.

Vielleicht können Sie mir den richtigen Weg weisen?

Antwort1

TOP="`pwd -P`" \
find . -type d -exec sh -c '
   for d
   do
      cd "$d" && \
         find . ! -name . -prune -type f -exec sh -c '\''
            while IFS=\; read -r pat repl
            do
               rename "s/$pat/$repl/g" "$@"
               N=$#
               for unmoved
               do
                  if [ -f "$unmoved" ]
                  then
                     set X ${1+"$@"} "$unmoved"
                     shift
                  fi
               done
               shift "$N"
               case $# in 0 ) break ;; esac
            done < patterns.csv
         '\'' x \{\} +
      cd "$TOP"
   done
' x {} +
  • Richten Sie findnur Netzverzeichnisse ein und laden Sie shsie in einem Rutsch herunter. Dies minimiert die Anzahl der Aufrufe von sh.
  • Richten Sie findin jedem dieser Verzeichnisse Netzdateien ein regular, und zwar nur auf einer Tiefe von 1, und geben Sie sie shin einem Gulp ein. Dadurch wird die Anzahl der renameAufrufe des Dienstprogramms minimiert.
  • Richten Sie eine whileSchleife ein, um die verschiedenen Paare einzulesen pattern <-> replacementund auf alle Dateien anzuwenden regular.
  • Im Verfahren rename-ingWir notieren, ob eine Datei nach dem renameVorgang noch vorhanden war. Wenn wir feststellen, dass eine Datei noch vorhanden ist, bedeutet dies, dass sie aus irgendeinem Grund nicht umbenannt werden konnte und daher in der nächsten pat/replIteration versucht wird. Wenn die Datei hingegen erfolgreich umbenannt wurde, wenden wir die nächste pat/replIteration NICHT auf diese Datei an, indem wir sie aus der Liste der Befehlszeilenargumente entfernen.

Antwort2

rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
   while read -r old new; do
      rename "s/$old/$new/i" "$@"
   done < "$rPairs"
' x {} +

Vorausgesetzt, Ihre Datei mit den Umbenennungspaaren enthält keine Nicht-ASCII-Zeichen und diese Datei wird außerdem außerhalb des Suchpfads abgelegt.

Antwort3

Nach Rakesh Sharmas Antwort bin ich nach ein bisschen weiterem Experimentieren und etwas Schlaf in die richtige Richtung gekommen.

Schließlich kam ich auf das folgende Skript:

#!/bin/bash


while IFS=";" read pattern replacement
do
  if [[ ! -z $pattern ]]
  then
    echo "Checking files for pattern '$pattern'."

    find ./files -name "*$pattern*" -type f | while read fpath
    do
      fname=$(basename "$fpath")
      dname=$(dirname "$fpath")

      echo "  Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
      mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
    done
  fi
done < patterns.csv

Es liest die Datei pattern.csvund durchläuft ihre Zeilen, wobei die Variablen $patternund ausgefüllt werden $replacement. Im zweiten Schritt ./fileswerden alle Dateien in einem Verzeichnis gefunden, die dem aktuellen Muster entsprechen. Dies muss getan werden, um zu vermeiden, dass erneut versucht wird, Dateien umzubenennen, wenn ein zweites Muster übereinstimmt, da dies fehlschlagen würde. Schließlich wird nur die Datei selbst umbenannt, nicht die Verzeichnisse, in denen sie enthalten ist, indem Shell-Parameter ersetzt werden.

Was nicht funktioniert, ist das Ersetzen der Übereinstimmungen ohne Berücksichtigung der Groß- und Kleinschreibung, aber damit kann ich leben.

Antwort4

Wichtig zu beachten ist, dass das Durchsuchen des Verzeichnisbaums ein langsamer Prozess ist und daher nur einmal durchgeführt wird. Wir sehen uns zunächst findnur die Verzeichnisse im Baum an. Und für jedes Verzeichnis suchen wir nach allen regular filesdarunter liegenden Verzeichnissen (keine Rekursion hier). Anschließend wenden wir die Umbenennungstransformation auf diese Dateinamen an und notieren uns gleichzeitig, ob dies erfolgreich war oder nicht. Wenn dies erfolgreich war, brechen wir aus der While-Schleife aus und verhindern so, dass das nächste Patt/Repl auf diese Datei angewendet wird.

tempd="`mktemp -d`" \
find . -type d -exec sh -c '
   cd "$1" && \
   for f in ./*
   do
      [ -f "$f" ] || continue
      while IFS=\; read -r patt repl
      do
         case $f in
            ./*"$patt"* )
               rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
               case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
         esac
      done < /tmp/patterns.csv
   done
' {} {} \;

verwandte Informationen