Shell-Skript zum Umbenennen mehrerer Dateien anhand der Namen ihrer übergeordneten Ordner

Shell-Skript zum Umbenennen mehrerer Dateien anhand der Namen ihrer übergeordneten Ordner

Ich habe eine Dateistruktur wie diese:

  • 00000010
    • 000000001.Datei1
    • 000000001.Datei2
  • 00000020
    • 00000003.Datei1
    • 00000003.Datei2
    • 00000003.Datei3
  • ...

Es gibt also Ordner mit 8-stelligen Namen, die eine oder mehrere Dateien enthalten, deren Namen mit 8-stelligen Zahlen beginnen. Aber diese Dateinamen sind – sagen wir mal – nicht synchron. Also versuche ich jetzt, sie in Bash rekursiv in „Archiv“ umzubenennen:

  • 00000010
    • 000000010.Datei1
    • 000000010.Datei2
  • 00000020
    • 00000020.Datei1
    • 00000020.Datei2
    • 00000020.Datei3
  • ...

Mein Skript sieht so aus:

#! /bin/bash

find * -maxdepth 1 -name "*" -type d | while read -r dir
do
        rename 's/$dir\/[0-9]{8}/$dir/' *
done

Aber das funktioniert nicht und gibt Fehler wie

Das globale Symbol „$dir“ erfordert einen expliziten Paketnamen in (eval 1) Zeile 1.

Wie könnte ich es schreiben, um die Dateien entsprechend ihren Ordnernamen umzubenennen?

Danke für die Hilfe!

Antwort1

Aus einer anderen Antwort erfuhr ich, dass ich

rename "s/$dir\/[0-9]{8}/$dir\/$dir/" $dir/*

Nur für den Fall, dass jemand das gleiche Problem hat ...

Antwort2

Mir gefällt die Antwort nicht wirklich, bei der $dirin das Muster interpoliert wird. Es könnte zu unerwarteten Ergebnissen führen, wenn das Verzeichnis beispielsweise ein Zeichen enthält, das als spezielles Regexp-Token interpretiert wird.

Ich würde die Dateien lieber direkt im Muster abgleichen, wodurch die Notwendigkeit entfällt, durch die Verzeichnisse zu schleifen und einfach einen normalen Glob zu verwenden.

rename 's#([^/]+)/[^/]+(\.file[0-9]+)$#$1/$1$2#' base_directory/*/*

wo der Glob die einzelnen Dateien innerhalb der Verzeichnisse trifft (es könnte sogar auf oder Ähnliches angegeben werden base_directory/*/*.file*, aber das sollte hier keine Rolle spielen).

Beachten Sie, dass dies der vollständige Befehl ist. Die findKonstruktion ist nicht erforderlich.

  • Ich verwende es #als Trennzeichen anstelle von „normal“, /um in den Mustern nicht aussteigen zu müssen.
  • Ich erfasse die (gierige) Gruppe von „kein Verzeichnistrennzeichen“, gefolgt von einem Verzeichnistrennzeichen, gefolgt von einem Dateinamen und einem Zeilenende. Dadurch wird die erste Erfassungsgruppe zum übergeordneten Verzeichnis für jede Datei.
  • Der erste Teil des Dateinamens wird mit Ausnahme der Endung verworfen, um das Ordinal-Suffix beizubehalten.

Ich habe mich für einen allgemeineren Ansatz entschieden, als davon auszugehen, dass alle Dateien 8 Zeichen lang sind. Der eigentliche Unterschied besteht jedoch darin, dass ich weder die Konstruktion benötige noch die Variable in die (möglicherweise unsichere) Regexp-Umgebung findhineinstopfen muss .$dir

verwandte Informationen