Shell-Skript zum Umbenennen mehrerer Dateien aus dem Namen ihrer übergeordneten Ordner

2478
Simon

Ich habe eine Dateistruktur wie folgt:

  • 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 Name mit 8-stelligen Nummern beginnt. Aber diese Dateinamen sind - sagen wir - nicht synchron. Nun versuche ich sie in bash für das Archiv rekursiv 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]/$dir/' * done 

Das funktioniert aber nicht und gibt Fehler wie

Das globale Symbol "$ dir" erfordert den expliziten Paketnamen in (eval 1) Zeile 1.

Wie kann ich es schreiben, um die Dateien nach ihren Ordnernamen umzubenennen?

Danke für die Hilfe!

3
Bitte nicht [cross-post] (http://stackoverflow.com/questions/4861208/shell-script-to-rename-multiple-files-fromtheir-parent-folders-name). Dennis Williamson vor 13 Jahren 1

2 Antworten auf die Frage

2
Simon

Wie aus einer anderen Antwort erfuhr ich, dass ich verwenden muss

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

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

0
Daniel Andersson

Ich mag die Antwort nicht wirklich, wo $dirin das Muster interpoliert wird. Dies kann 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, die Notwendigkeit, die Verzeichnisse zu durchlaufen und einen regulären Glob zu verwenden, aufheben.

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

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

Beachten Sie, dass dies der vollständige Befehl ist, ohne dass die findKonstruktion erforderlich ist .

  • Ich verwende #als Trennzeichen anstelle des "regulären" /, um zu vermeiden, dass das in den Mustern entkommen muss.
  • Ich fange die (gierige) Gruppe von "kein Verzeichnisseparator" ein, gefolgt von einem Verzeichnisseparator, 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 verworfen, mit Ausnahme der Endung, um das Suffix der Ordinalzahl beizubehalten.

Ich habe mich für einen allgemeineren Ansatz entschieden, als davon auszugehen, dass die Dateien alle acht Zeichen lang waren. Der wirkliche Unterschied besteht jedoch darin, dass ich weder das findKonstrukt brauche, noch die $dirVariable in die (möglicherweise unsichere) Regex-Umgebung packen muss.