alexander01
Goto Top

Script zur Umsortierung von Bild-Dateien unter Windows

Hallo,
ich bitte um Hilfe bei der Erstellung eines komplexeren Scriptes (batch-Datei). Es soll unter Windows 10 laufen und Bilddateien deutlich einfacher auffindbar und aufrufbar machen.

aktuelle Situation:
- auf einem Laufwerk gibt es ca. 10.000 Ordner, unregelmäßig (nicht fortlaufend) nummeriert, also 1, 2, 4, 7, 12 u.s.w.
- das Script soll, sagen wir immer 100 dieser Ordner (damit ich den Erfolg immer rechtzeitig prüfen und ggf.abbrechen kann), die ich im Script eintrage (also z.b. ordner 201 bis 300) bearbeiten
- in jedem dieser Ordner gibt es ein oder mehrere Unterverzeichnisse, deren Namen das Erstellungsdatum (leider) im Format M.T.J (z.B. 4.25.2015) und weiteres (sicher die Uhrzeit) enthält.
- dieses Datum ist wichtig, das Script soll es auslesen und in derselben Verzeichnisebene einen neuen Ordner mit dem Namen JJJJ-MM-TT erstellen
- im Original-Ordner (M.T.J, siehe oben) gibt es immer 4 Unterordner. Von diesen 4 Unterordnern heißt einer "Images"
- in diesem Unterordner liegen neben anderen Dateien und weiteren Unterordnern die Bilddateien (im .tif - Format).

ich möchte, das:
- alle diese .tif-Dateien in den neu erstellen Ordner mit dem korrekten Namen ( JJJJ-MM-TT) verschoben werden
- möglichst soll auch der Dateiname verändert werden "Image1" bis ImageXY
- alle Unterordner und Dateien , die im Originalen Ordner (M.T.J) liegen, sollen nachfolgend gelöscht werden, einschließlich dieses Ordners selber.

Zur Veransschaulichung:

(Laufwerk) K:\

(Ordner)1
4.25.2015-fefvvvvv
Unterordner1
Unterordner2
Images
Order xyz
Ordner abc
Datei xyz
imagexyz.tif
imageabc.tif
Unterordner4

soll werden zu:

(Ordner)1
2015-04-25
image1
image2

Ich würde denken, als erstes muß das Script das Datum des Originalordners erkennen und einen korrekten neuen Ordner erstellen.
Danach muß das Script im richtigen Unterordner (images) alle .tif-Dateien dahin verschieben, gleichzeitig umbenennen
Ich möchte eine gewisse Steuerung behalten, das Script soll erstmal von ordner 1 bis 100 arbeiten, danach 101 bis 200 u.s.w.

Ich bin über Hilfe sehr dankbar!
Alexander

Content-ID: 577783

Url: https://administrator.de/contentid/577783

Ausgedruckt am: 22.11.2024 um 19:11 Uhr

erikro
erikro 09.06.2020 um 22:00:48 Uhr
Goto Top
Moin,

ein schönes Projekt, um programmieren zu lernen. Wenn Du es wirklich hard core willst, mache es mit einem batch. Wenn Du es sehr viel logischer, besser aufgebaut und vor allem mächtiger willst, dann nimm dafür die Powershell.

https://docs.microsoft.com/de-de/powershell/

Da guckst du nach den Befehlen get-childitem, get-date, der Methode split(), Schleifenprogrammierung und los geht's. Ich sitze gerade am falschen Rechner. Da habe ich den Link nicht. Aber ich empfehle gerne noch ein open book. Guck mal in älteren Beiträgen von mir zum Thema Powershell.

Wenn Du dann ein paar Zeilen hast, frage gerne wieder.

Liebe Grüße

Erik
Shadowminder
Shadowminder 13.06.2020 um 11:07:15 Uhr
Goto Top
Hallo alexander01,

ich schau mal was sich mit Batch machen lässt, wenn ich ein fertiges Produkt habe melde ich mich!

LG

Shadowmind
alexander01
alexander01 14.06.2020 um 18:32:38 Uhr
Goto Top
Danke.
ich habe grundsätzlich wenig Übung im programmieren, entwickle jedoch im Kopf einen Plan, den ich sonst Schritt für Schritt umsetze.
Würde mich natürlich freuen über die Hilfe !!
erikro
erikro 14.06.2020 um 21:47:35 Uhr
Goto Top
Zitat von @Shadowminder:
ich schau mal was sich mit Batch machen lässt, wenn ich ein fertiges Produkt habe melde ich mich!

Wer das heute statt mit der Powershell mit der alten Batchsprache macht, ist eindeutig ein Masochist. face-wink
colinardo
colinardo 20.06.2020 aktualisiert um 15:23:12 Uhr
Goto Top
Servus Alexander,
probiere mal das folgende Powershell-Skript ob das deinen Erwartungen entspricht (Pfad in Zeile 2 bitte anpassen)
# Pfad bitte anpassen
$root = 'K:\Bilder'  
$batch = 0
ls $root -Directory | %{
    if ($batch % 100 -eq 0){
        Read-Host "`nEnter drücken zum fortfahren für den nächsten Batch an Ordnern"  
    }
    ls $_.FullName -Directory | ?{$_.Name -match '(\d{1,2})\.(\d{1,2})\.(\d{4})'} | %{  
        $newfolder = (Join-Path $_.Parent.FullName ([datetime]::ParseExact($matches,'M.d.yyyy',[cultureinfo]::InvariantCulture).toString('yyyy-MM-dd')))  
        md $newfolder -Force | out-null
        $cnt = 1
        ls "$($_.FullName)\Images" -File -Filter *.tif | %{  
            move-item $_.Fullname -Destination "$newfolder\$($_.BaseName)$cnt$($_.Extension)" -Verbose  
            $cnt++
        }
        del $_.FullName -Force -Recurse
    }
    $batch++
}

Ich möchte eine gewisse Steuerung behalten, das Script soll erstmal von ordner 1 bis 100 arbeiten, danach 101 bis 200 u.s.w.
Ich hab dir das zwar oben eingebaut, aber sowas macht man eigentlich immer erst mal in einem Test-Ordner in den man die Quell-Daten hineinkopiert und dann das Skript darauf loslässt.
Alternativ kann das die Powershell auch alles simulieren und nur anzeigen was sie tun würde. Dafür haben fast alle CMDLets den Parameter -whatif mit dem man auch gut sehen kann ob alles so läuft wie gewünscht.

Grüße Uwe
alexander01
alexander01 21.06.2020 um 22:26:50 Uhr
Goto Top
Danke, ich probiere es aus und melde mich
Grüße
Alexander
alexander01
alexander01 22.06.2020 um 22:11:54 Uhr
Goto Top
Als Erstes: Vielen Vielen Dank !!
Das Script funktioniert! Ich freue mich, es erspart eine Unmenge Zeit!
Ich bin sprachlos, mit wie wenig Zeilen man das programmieren kann, ich hätte ein sehr umfangreiches Script erwartet / befürchtet.
ich befasse mich mit dem Inhalt!

Vielleicht gelingt es noch, zwei Kleinigkeiten anzupassen:
Das Wichtige:
In der Ebene des Ordners, dessen Name umbenannt wird (das Datum im Formal YYYY-MM-dd) gibt es noch mehrere Steuer-Dateien, die alle gelöscht werden können. nur der Ordner, der umbenannt ist und die Bild-Dateien enthält, soll bestehen bleiben.
das weniger Wichtige:
wie gelingt es, die Bild-Dateien umzubenennen? Fortlaufend, z.B. YYYY-MM-DD_Image1, YYYY-MM-DD_Image2 etc.?
Im Moment haben sie kryptische Bezeichnungen, die eher irritieren...
Wenn ich Verzeichnisse über "100" durchlaufen lassen möchte, passe ich das Script nur an?
Es gibt etwa 20.000 Verzeichnisse, ich würde das Script (es arbeitet sehr rasch!) Blöcke von 100 oder 1000 Verzeichnissen korrigieren lassen.
Nochmals: Danke, Uwe!!
Alexander
colinardo
colinardo 23.06.2020 aktualisiert um 09:09:30 Uhr
Goto Top
In der Ebene des Ordners, dessen Name umbenannt wird (das Datum im Formal YYYY-MM-dd) gibt es noch mehrere Steuer-Dateien, die alle gelöscht werden können.
wie gelingt es, die Bild-Dateien umzubenennen? Fortlaufend, z.B. YYYY-MM-DD_Image1, YYYY-MM-DD_Image2 etc.?

Habe dir mal als erste Hilfe Kommentare ergänzt.
# Pfad bitte anpassen
$root = 'K:\Bilder'  
$batch = 0
# Abfrage aller Ordner der ersten Ebene im Root-Ordner
ls $root -Directory | %{
    # alle 1000 Ordner anhalten und fragen
    if ($batch % 1000 -eq 0){
        Read-Host "`nEnter drücken zum fortfahren für den nächsten Batch an Ordnern"  
    }
    # Unterordner abfragen deren Namen dem Datumsmuster entspricht
    ls $_.FullName -Directory | ?{$_.Name -match '(\d{1,2})\.(\d{1,2})\.(\d{4})'} | %{  
        # Datum neu formatieren und an Variable übergeben
	$d = [datetime]::ParseExact($matches,'M.d.yyyy',[cultureinfo]::InvariantCulture).toString('yyyy-MM-dd')  
        # neuen Ordner eine Ebene höher erstellen
        $newfolder = (Join-Path $_.Parent.FullName $d)
        md $newfolder -Force | out-null
        # Zähler initialisieren
        $cnt = 1
        # Alle *.tif Dateien des Unterordner "Images" in den neuen Ordner unter neuem Namen verschieben 
        ls "$($_.FullName)\Images" -File -Filter *.tif | %{  
            move-item $_.Fullname -Destination "$newfolder\${d}_Image$cnt$($_.Extension)" -Verbose  
            $cnt++
        }
        # alten Ordner rekursiv löschen
        del $_.FullName -Force -Recurse
    }
    # evt. Steuerdateien löschen
    ls $_.Fullname -File | del -Force

    $batch++
}
Grüße Uwe
alexander01
alexander01 24.06.2020 um 07:50:52 Uhr
Goto Top
perfekt, funktioniert.
Nochmals Danke, Uwe.
Ich denke darüber nach, die umbenannten Ordner direkt auf der ersten Verzeichnis-Ebene des Laufwerkes zu speichern.
(also direkt unter z.B. K:\)
Ich denke, in diesem Fall müsste ich die Zeile $newfolder = (Join-Path $_.Parent.FullName $d) anpassen.
Weil sich $_.Parent.FullName geändert hat.
Ich versuche dieser Tage einmal mein Glück an dieser letzten Sache.
Generell erstmal Danke, es funktioniert.
Grüße
Alexander
alexander01
alexander01 29.06.2020 um 19:38:29 Uhr
Goto Top
brauche offensichtlich doch noch mal Hilfe.
Ich bin soweit, das ich weiß, das:

Join-Path $_.Parent.FullName $d

den neuen Ordnernamen erstellt

$_.Parent.FullName heißt also z.B. K:\Bilder\2 oder K:\Bilder\27 o.ä.

schön wäre, der neue Pfad heißt K:\2 oder K:\27 o.ä.
Ich muß also die Variable $_.Parent.FullName zerlegen und brauche den Anfang (Laufwerk) und das Ende.
Offensichtlich benötigt man Split-Path. Hier wird's für mich chaotisch.

Könnte mir jemand Starthilfe geben?
Danke
Alexander
colinardo
colinardo 29.06.2020 aktualisiert um 19:55:54 Uhr
Goto Top
Offensichtlich benötigt man Split-Path.
Nein, viel einfacher.
Lass dir einfach mal auf der Konsole die verfügbaren Eigenschaften des Objekts in der Schleife anzeigen
# ....
$_.Parent | Get-Member *
# ....
Dann wirst du in der Auflistung sehen das Objekt vom Typ System.IO.DirectoryInfo ebenfalls wieder eine Eigenschaft Namens Parent besitzt die den Ordner wieder eine ebene Höher aufruft, von diesem kannst du dann ebenfalls wieder die FullName Eigenschaft abrufen und landest somit zwei Ebenen höher face-smile

screenshot

Ergo
Join-Path $_.Parent.Parent.FullName $d

Du kannst natürlich auch "K:\" als festen Pfad in der Angabe nutzen, dann ist das Skript aber abhängig von der Verfügbarkeit des Pfades und man muss es manuell ändern wenn man es woanders anwendet.

Grüße Uwe

Wenns das dann war, den Beitrag bitte noch auf gelöst setzen, und Lösungen markieren. Merci.
alexander01
alexander01 30.06.2020 um 12:11:14 Uhr
Goto Top
Das klappt nicht, Uwe. Das Sript erstellt jetzt die Datums-Ordner eine Ebene höher. Ich möchte aber, das der Ordner der 3.ebene (die Zahl) nun 1. ebene wird. Das root verzeichnis hat ordner erster (Bilder) und zweiter ebene (studies).

K:\Bilder\studies\1 und so weiter

Daraus soll Werden

K:\1

Unter „1“ sollen dann die Datumsordner liegen, darin jeweils die Bilder.
Ich muss also 2 Ebenen höher und den Zahl-Ordner mitnehmen.
colinardo
Lösung colinardo 30.06.2020 um 12:27:06 Uhr
Goto Top
Naja, mit dem Handwerkszeug was du jetzt hast bekommst du diesen Rest auch noch selbst hin 😉.
alexander01
alexander01 30.06.2020 um 12:34:50 Uhr
Goto Top
Verstehe. Arbeite heute oder morgen nochmal daran.
Grüße
Alexander
alexander01
alexander01 02.07.2020 um 21:26:42 Uhr
Goto Top
Gelingt mir nicht. Googeln bringt keine Lösung. Es ist einfach, zum übergeordneten Verzeichnis zu wechseln, aber den abgeschnittenen (letzten) Verzeichnisnamen zu übernehmen (in einer Variable zu speichern) und an den laufwerknamen zu hängen, gelingt mir nicht. Bin über Hinweise dankbar. Ich suche parallel weiter.
Gruß
Alexander
alexander01
alexander01 04.09.2020 um 09:04:50 Uhr
Goto Top
Hallo, Uwe,
es gelingt mir noch nicht so richtig, das Regelwerk Powershell zu begreifen.
Kannst Du mir noch mal bitte Hilfestellung geben.
Ich beziehe mich auf das von Dir entwickelte Script weiter oben.

ls $_.FullName -Directory | ?{$_.Name -match '(\d{1,2})\.(\d{1,2})\.(\d{4})'} | %{

Verstehe ich das richtig:
diese Zeile listet alles (Dateien und Unterverzeichnisse) in einem Ordner auf, übergibt jedesmal den gefundenen Namen an eine Analyse:

?{$_.Name -match '(\d{1,2})\.(\d{1,2})\.(\d{4})'}

hier wird geprüft, ob der übergebene String aufgebaut ist nach dem Prinzip xx.xx.xxxx

Wenn dies zutrifft, wird der String übergeben an den Rest im Script:

  1. Datum neu formatieren und an Variable übergeben
$d = [datetime]::ParseExact($matches,'M.d.yyyy',[cultureinfo]::InvariantCulture).toString('yyyy-MM-dd')
                  1. neuen Ordner eine Ebene höher erstellen
                  $newfolder = (Join-Path $_.Parent.FullName $d)
                  md $newfolder -Force | out-null
                  und so weiter.

                  Hintergrund ist folgender:
                  ich habe noch einen (deutl. kleineren) Ordner zum umbenennen. Die Ordner sind anders benannt (name1name2_xxxx), in diesen Ordnern liegen die Bilder.
                  Es würde vielleicht reichen, wenn ich diesen Code

                  ?{$_.Name -match '(\d{1,2})\.(\d{1,2})\.(\d{4})'}

                  anpasse?

                  Danke für jegliche Hilfe.
                  Gruß
                  Alexander