Übergeordnetes und untergeordnetes Verzeichnis mit gleichem Namen, Dateien in übergeordnetes Verzeichnis verschieben

Übergeordnetes und untergeordnetes Verzeichnis mit gleichem Namen, Dateien in übergeordnetes Verzeichnis verschieben

Ich brauche eine Möglichkeit, Verzeichnisse nach Unterverzeichnissen mit demselben Namen zu durchsuchen und dann alle Dateien im Unterverzeichnis in das übergeordnete Verzeichnis zu verschieben. /recup-dir1/recup-dir1/files to /recup-dir1/files. Die Unterverzeichnisse können also leer gelassen werden, da ich so etwas wie find . -type -d -empty -delete das Löschen aller leeren Verzeichnisse verwenden kann.

Das Problem ist also, dass ich keine Ahnung habe, in welchen Verzeichnissen sich Unterverzeichnisse mit dem gleichen Namen befinden und in welchen nicht.

In Pseudocode brauche ich so etwas.

While more directories are unchecked
get name-x of  next dir
   enter dir  
   If name-x/name-x exist
   move all files in name-x/name-x to name-x
   mark dir as done
next 

Meine beste Vermutung ist, ein kleines Python-Skript zu erstellen, um eine Liste aller Verzeichnisse zu erstellen, die ein untergeordnetes Verzeichnis mit demselben Namen haben, und diese Liste durch einen Befehl wie diesen zu durchlaufen find something something -exec mv

Vielleicht könnte dies mit Bash-Skripten erledigt werden, oder es gibt eine andere Lösung. Wie etwa ein Rsync-Befehl. Da ich dieses Chaos jedoch wahrscheinlich mit Rsync verursacht habe, glaube ich nicht, dass das die Lösung sein wird.

Bearbeiten: Hier ist ein tatsächlicher Teil der Baumausgabe: Die Verzeichnisse der obersten Ebene befinden sich in /mnt/external-disk/tst-backup. Auf niedrigeren Ebenen gibt es keine Unterverzeichnisse.

│   └── recup_dir.1
├── recup_dir.10
│   └── recup_dir.10
├── recup_dir.100
│   └── recup_dir.100
├── recup_dir.102
│   └── recup_dir.102
└── recup_dir.1020
    └── recup_dir.1020

Antwort1

Mit zshkönnen Sie Folgendes tun:

for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
  contents=($dir/*(NDoN))
  (( $#contents == 0 )) ||
    mv -- $contents $dir:h/ &&
    rmdir -- $dir
done

Wo:

  • **/*(qualifiers)rekursives Globbing mit Glob-Qualifizierern
  • N: nullglob: beschwere dich nicht, wenn es keine Übereinstimmung gibt
  • D: dotglob: versteckte Dateien einschließen
  • od: Sortieren Sie zuerst nach der Tiefe (Blätter vor den Zweigen, auf denen sie sich befinden).
  • oN: Machen Sie sich ansonsten nicht die Mühe, die Dateiliste zu sortieren.
  • /: Beschränkung auf Dateien vom Typ „Verzeichnis“.
  • e['expression']: Beschränkung auf Dateien, für die der expressionCode „true“ zurückgibt (in denen der aktuelle Dateipfad gespeichert ist $REPLY).
  • $REPLY:t: Ende (Basisname) der Datei
  • $REPLY:h:t: Ende des Kopfes (Verzeichnisname) der Dateien)

Mit bash4.4+ und GNU findoder den findmeisten BSDs könnten Sie etwas Ähnliches tun mit:

shopt -s nullglob dotglob
readarray -td '' dirs < <(
  LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
  contents=("$d"/*)
  (( ${#contents[@]} == 0 )) ||
    mv -- "${contents[@]}" "${d%/*}/" &&
    rmdir -- "$d"
done

./path/to/dir/dirDieses Mal wird ein regulärer Ausdruck verwendet, um die Dateien mithilfe grundlegender regulärer Ausdrucks-Rückverweise abzugleichen .

Antwort2

Versuchen Sie dies, basierend auf GNU findv4.8.0 und Bash v5.1.8

Teil 1: Verzeichnisbaum analysieren + Duplikate von Unterverzeichnisnamen erkennen

Angenommen, ein bestimmtes Verzeichnis in Ihrem Baum hat die folgende Struktur:

./
|__test1/
     |__dirname with space
     |           |__test2
     |                |__ test2
     |__dirname **
     |       |__test1
     |
     |__reboot
     |     |__test1
     | 
     |__test2/
          |__test3/
               |__test2/
                    |__test1/
                         |__test1/

(Seltsame Verzeichnisnamen dienen der Demonstration der Codesicherheit.)

Sie sehen, dass einige Unterverzeichnisse (Subdirs) auf unterschiedliche Weise wiederholt werden. Einige werden mehrfach wiederholt, nicht nur einmal (z. B. test1), eines wird nicht wiederholt ( test3), und sie können entweder als übergeordnetes und untergeordnetes Verzeichnis wiederholt werden oder durch eine beliebige Anzahl dazwischenliegender Unterverzeichnisse getrennt sein.

Der folgende Code deckt Duplikate von Unterverzeichnisnamen in einer Verzeichnisstruktur detailliert auf.

  • es analysiert den Dateibaum für die Unterverzeichnisstruktur beginnend bei$PWD
  • Es findet Duplikate für alle Komponenten eines Unterverzeichnispfads mit 2 oder mehr Ebenen, wobei die Stammebene nicht mitgezählt wird, die ist $PWD. In meinem Experiment ist der längste Unterverzeichnispfad: ./test1/test2/test1/test3/test2/test1/test1
  • Es druckt das erste Duplikat des Unterverzeichnisses, das auf jeder Unterverzeichnisebene gefunden wird, beginnend beim Blatt, d. h. es liest den Unterverzeichnispfad von rechts nach links.
  • Der Druckvorgang wird in umgekehrter Reihenfolge auf eine Datei umgeleitet, sodass der längste Unterverzeichnispfad zuerst angezeigt wird. Zwei aufeinanderfolgende Semikolons trennen die Pfadkomponenten (links vom ";;") vom ersten Duplikat (rechts vom ";;"), das gemäß dem vorherigen Aufzählungspunkt gefunden wurde.

[Code]

$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe"  ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data

$ cat -n  tmp.data

1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1

Teil 2: Verarbeitung von Duplikaten von Unterverzeichnisnamen; Verschieben von Inhalten

Die Abarbeitung erfolgt in der in angezeigten Reihenfolge tmp.data.

  • In der ersten Zeile von ist tmp.datader erste Name des Duplikats im Pfad . Wir können seinen Inhalt auf die am weitesten links stehende Unterverzeichnisebene mit demselben Namen übertragen:./test1/test2/test1/test3/test2/test1/test1test1./test1/
  • Sobald der Inhalt verschoben wurde, ohne dass vorhandene Dateien am Ziel überschrieben wurden, wird die äußerste rechte Unterverzeichnisebene test1gelöscht.
  • Wir fahren mit Zeile 2 fort tmp.dataund wiederholen die obigen Schritte.
  • usw., bis alle Zeilen tmp.dataverbraucht sind.

An dieser Stelle lautet die Frage (an den Autor der Frage: @TomDerks), was mit dem ganz rechts test1/*in Zeile 6 zu tun ist. Sollteallesein Inhalt in das am weitesten links stehende Verzeichnis mit dem gleichen Namen verschoben werden, das in diesem Fall die erste Unterverzeichnisebene im Pfad ist? Schließt "alle" Dateien ein in./test1/test2/test1/ sowiedas Unterverzeichnis test3und dessen Inhalt?
Davon hängt die vollständige Lösung (Teil 2) ab.

verwandte Informationen