hopfman
Goto Top

Musterlösungen: Dateien verschieben abhängig vom Teil des Dateinamens

Ich habe eine ständig wachsende Anzahl von Dateien aus unterschiedlichen Quellen, die in Unterverzeichnisse verschoben werden sollen. So weit so einfach.

Die Herkunft der Dateien ist aus einem Anfangsstring unterschiedlicher Länge erkennbar, dann kommt ein eindeutiger Separator und dann weitere Dateiangaben. Die Dateien sollen in ein Unterverzeichnis (das ggf. noch nicht existiert) und dem Part des Dateinamens entspricht, der vor dem Separator kommt.

Beispiel:
1 m1234__20160309.log
2 m1234__20160308.log
3 m34xl2__0900-20160308.avi
4 m34xl2__20160308.txt
5 n890__tv21000.jpg

Datei 1 und 2 in Verzeichnis m1234
Datei 3 und 4 in Verzeichnis m34xl2
Datei 5 im Optimalfall nicht verschieben (da nur eine Datei mit Anfang n890 existiert), oder ohne Prüfung ins Verzeichnis n890.

Die einfache Variante extrahiert also den vorderen Teil einer Datei und verschiebt die Datei dann in ein Unterverzeichnis, das heißt wie der extrahierte Teil.

Die Optimalvariante prüft quasi auf "Teildubletten" und verschiebt nur die, und lässt die Einzelfälle aus.

Ich habe die Hilfe durchsucht und auch gegoorgelt, aber immer nur Varainten gefunden, bei denen der Anfang zumindest von der Länge her gleich war, das ist hier aber nicht der Fall.

Vielleicht weiß hier ja jemand, wie man das am einfachsten machen kann.

Danke

Content-ID: 298655

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

Ausgedruckt am: 21.11.2024 um 19:11 Uhr

114757
114757 09.03.2016, aktualisiert am 10.03.2016 um 08:34:06 Uhr
Goto Top
Moin.
Powershell z.B.
# Alle Dateien des Ordners nach dem ersten Teil des Dateinamens gruppieren (wird über Regular Expressions und Group-Matching gemacht). dann werden nur die Gruppen verschoben von denen es mindestens 2 Dateien gibt
gci "C:\quellordner\*.*" | group {[regex]::match($_.Basename,'^(.*?)__').Groups[1].Value} | ?{$_.Count -ge 2} | %{  
    # Zielordner der Gruppe festlegen
    $targetfolder = "C:\quellordner\$($_.Name)"  
     # wenn Zielordner noch nicht existiert erstelle ihn
     if(!(Test-path $targetfolder)){md $targetfolder}
    # Alle Dateien der Gruppe in den Zielordner verschieben
    move-item $_.Group -Destination $targetfolder -Force
}
gegoorgelt
Hoffentlich auch ordentlich ausgespuckt face-smile

Gruß jodel32
bastla
bastla 09.03.2016 um 21:05:18 Uhr
Goto Top
Hallo Hopfman und willkommen im Forum!

Als Batch schematisch etwa so:
@echo off & setlocal
set "Ordner=D:\Dein Ordner"  

pushd "%Ordner%"  
for /f "delims=" %%i in ('dir /b *_*') do (  
    for /f "delims=_" %%a in ("%%i") do (  
        for /f %%n in ('dir "%%a_*.*"2^>nul^|findstr "Datei(en)"') do if %%n gtr 1 (  
            md "%%a" 2>nul  
            move "%%a_*.*" "%%a">nul  
        )
    )
)
popd
Wenn Du Dateien, die verschoben werden, angezeigt haben möchtest, lass am Ende der Zeile 9 ">nul" weg.

Grüße
bastla
TlBERlUS
TlBERlUS 09.03.2016 aktualisiert um 22:02:05 Uhr
Goto Top
Nabend,

Powershell:

# Gibt den Quellpfad der Dateien an
$source = "C:\test"  

# Schreibt alle Dateien auf der obersten Ebene in ein Array
$files = gci $source | ?{$_.Mode -eq '-a---'}  

foreach ($f in $files){
    # Spaltet den String an einer beliebigen Stelle auf, an der der Separator vorhanden ist
    $dublette = $f -split '__'  
    # Zähler, wie häufig das erste Element vorhanden ist
    $counter = $files -match $dublette
    # Definieren des Zielpfades
    $destpath = $source +"\" +$dublette  
    # Wenn eine Dublette vorhanden ist und das Verzeichnis noch nicht vorhanden ist, wird es erstellt und alle Dateien mit diesem Schema werden dorthin verschoben
    if ( $counter.Count -gt 1){
        $search = Test-Path $destpath
        if ($search -eq $false){
            New-Item $destpath -ItemType directory
        }
    Move-Item $source\$f -Destination $destpath
    } 
}

Edit: @114757 mal wieder schneller unterwegs mit der Powershell als ich; und dazu noch kürzer face-smile
Biber
Biber 09.03.2016 um 21:44:46 Uhr
Goto Top
[OT]
Moin alle,

na kommt, jetzt noch einen (vorzugsweise kommentierten) VBS-Schnipsel dazu, dann mach ich einen "Tipp" aus den ganzen Musterlösungen.

Und lass ihn als Referenz für künftige verzweifelte GoorglerInnen stehen.

Ich warte....

Grüße
Biber
P.S. An manchen Tagen ist es mir eine Freude, im Bereich "Entwicklung" mitzulesen.
[/OT]
Hopfman
Hopfman 09.03.2016 um 21:45:20 Uhr
Goto Top
Vielen lieben Dank, perfekt!

Und während ich bastlas batch noch einigermaßen verstehe, wird Powershell für mich immer ein Buch mit 7 Siegeln bleiben...
TlBERlUS
TlBERlUS 09.03.2016 aktualisiert um 21:56:38 Uhr
Goto Top
Zitat von @Biber:
na kommt, jetzt noch einen (vorzugsweise kommentierten) VBS-Schnipsel dazu, dann mach ich einen "Tipp" aus den ganzen Musterlösungen.
Kann ich leider nicht mit dienen face-smile
Und lass ihn als Referenz für künftige verzweifelte GoorglerInnen stehen.
Soll man für den Lern-Effekt den Code kommentieren?
Edit: Lesen sollte man nochmal üben -.-
P.S. An manchen Tagen ist es mir eine Freude, im Bereich "Entwicklung" mitzulesen.
Warum das?
TlBERlUS
Lösung TlBERlUS 09.03.2016 um 21:47:46 Uhr
Goto Top
Zitat von @Hopfman:

Vielen lieben Dank, perfekt!

Und während ich bastlas batch noch einigermaßen verstehe, wird Powershell für mich immer ein Buch mit 7 Siegeln bleiben...
och, ist eig. wesentlich einfacher als Batch.

Magst du den Thread dann noch als gelöst markieren?
Hopfman
Hopfman 09.03.2016 um 21:49:50 Uhr
Goto Top
Auch hier: Funktioniert perfekt. Einziges Manko ist, dass der Separator nicht nur an einer Stelle zu ändern ist, das ist beim PS-Script natürlich eleganter. Ich habe auch nicht erwähnt, dass der Separator in verschiedenen Verzeichnissen wechselt (zB __ oder ' -- ' mit Leerstellen etc.)

Auf jeden Fall seid ihr echt fix, toll!

Ich möchte gar nicht sagen, wie lange ich die Dateien bis jetzt von Hand verschoben habe. face-confused
bastla
bastla 10.03.2016 aktualisiert um 01:09:03 Uhr
Goto Top
Hallo Hopfman!

Wenn der Separator mehrere Zeichen umfasst, müsste der Batch anders gestaltet werden - ansonsten lässt sich aber zumindest mit einer Variablen die Anpassung vereinfachen (auf speziellen Wunsch ausnahmsweise sogar mit Kommentaren face-wink):
@echo off & setlocal
set "Ordner=D:\Dein Ordner"  
set "Separator=_"  

REM voruebergehend in den angegebenen Ordner wechseln
pushd "%Ordner%"  
REM in einer Schleife alle passenden Dateien (mit dem Separator im Namen; keine Ordner) abarbeiten
for /f "delims=" %%i in ('dir /b/a-d "*%Separator%*"') do (  
    REM Anfangsstring isolieren
    for /f "delims=%Separator%" %%a in ("%%i") do (  
        REM Anzahl der mit dem Anfangsstring beginnenden Dateien ermitteln und
        REM wenn mehr als eine Datei vorhanden ist ...
        for /f %%n in ('dir "%%a%Separator%*.*"2^>nul^|findstr "Datei(en)"') do if %%n gtr 1 (  
            REM ... den Zielordner erstellen (falls bereits vorhanden Fehlermeldung nicht anzeigen) ...
            md "%%a" 2>nul  
            REM ... und alle entsprechenden Dateien in den Zielordner verschieben
            move "%%a%Separator%*.*" "%%a">nul  
        )
    )
)
REM Wechsel des Ordners rückgängig machen
popd
Grüße
bastla

P.S.: Als unkommentierte (und nur oberflächlich getestete) Draufgabe auch noch die Variante mit erweitertem Separator:
@echo off & setlocal
set "Ordner=D:\Dein Ordner"  
set "Separator= - "  

pushd "%Ordner%"  
for /f "delims=" %%i in ('dir /b/a-d "*%Separator%*"') do (  
    set "Name=%%~ni"  
    setlocal enabledelayedexpansion
    call set "Anfang=%%Name:%Separator%!Name:*%Separator%=!=%%"  
    for /f %%n in ('dir "!Anfang!%Separator%*.*"2^>nul^|findstr "Datei(en)"') do if %%n gtr 1 (  
        md "!Anfang!" 2>nul  
        move "!Anfang!%Separator%*.*" "!Anfang!">nul  
    )
    endlocal
    )
)
popd

P.P.S.: @ Biber
Als VBS braucht das (wegen der Dateizählung - ein Pendant zu dir "* - *.*" gibt es mE ja leider nicht) ewig face-sad:
Ordner = "D:\Dein Ordner"  
Separator = " - "  

Set fso = CreateObject("Scripting.FileSystemObject")  
For Each File In fso.GetFolder(Ordner).Files
    If InStr(File.Name, Separator) > 0 Then    
        Anfang = Split(File.Name, Separator)(0)
        L = Len(Anfang & Separator)
        Anzahl = 0
        For Each Testfile In fso.GetFolder(Ordner).Files
            If LCase(Left(TestFile.Name, L)) = LCase(Anfang & Separator) Then Anzahl = Anzahl + 1
        Next
        If Anzahl > 1 Then
            Ziel = Ordner & "\" & Anfang  
            If Not fso.FolderExists(Ziel) Then fso.CreateFolder(Ziel)
            fso.MoveFile Ordner & "\" & Anfang & Separator & "*", Ziel & "\"  
        End If
    End If
Next
Biber
Biber 10.03.2016 aktualisiert um 08:24:07 Uhr
Goto Top
[OT ii]
Moin alle,

ich danke nochmals euch unermüdlichen Skriptern und ändere diese "Frage" mal zu einem "Tipp" um.
Denn ich denke, diese Muster-Skizzen werden auch noch anderen hilfreich sein.

Zum der ewigen Kontroverse "Kommentieren von Code" vs "Mein Schnipsel ist doch selbsterklärend"...

ich sach ma' so.... ich schreibe am liebsten Oneliner... da gehe ich der Diskussion aus dem Weg.

Was immer für eine Kommentierung spricht: wenn es ganz blöd läuft, dann muss auch ich mal einen mehrzeiligen Schnipsel anpassen....
Aber dreieinhalb Jahre, nachdem ich ihn mal flüssig runtergetippt habe (sofern ich meine Tastaturfertigkeiten als flüssiges Tippen bezeichnen kann), brauche ich erstmal ein paar Minuten, um überhaupt nachzuvollziehen, WTF ich mir denn dabei gedacht habe.

Andererseits...
Ich liebe auch selbstdokumentierenden Code und "sprechende Variablen" wie strDayOfWeek = 42;. Vor allem, wenn ich so einen "geerbten" Schnipsel mal warten muss.

Aber was soll's... Unsere Kantine dokumentiert auch nicht jede Zutat im Nudelauflauf.

Grüße
Biber
[/OT ii]
.
114757
114757 10.03.2016 aktualisiert um 14:22:58 Uhr
Goto Top
Ein gutes hat das weglassen der Doku hier aber manchmal, die Leute sind gezwungen sich damit auseinanderzusetzen, statt oft nur copy n' paste zu betreiben, ist zwar für den TO etwas mehr Aufwand aber auch das kann einen positiven Lerneffekt für den TO bedeuten face-wink

Ist zwar bei Powershell Onlinern etwas aufwendiger zu dokumentieren, da man einen Roman vor die Zeile schreibt, aber dem guten Willen habe ich die Zeilen oben auch noch nachträglich kommentiert.

Aber ich sehs schon voraus wenn wir auf den Beitrag hier verweisen, kommt zu 95% wieder die Antwort von den meisten:
"Aber bei mir ist das doch ganz anders ?" face-wink
Tomilai
Tomilai 11.09.2016 um 09:38:49 Uhr
Goto Top
Hallo Bastla,
vielen Dank für diese Lösung. Mein Problem ist ähnlich: Das Kriterium bei mir ist der Teil nach dem Unterstrich.
Z.B:
Original Ordner:
Berta_PC01.txt
Berta_PC02.txt
Berta_PC03.txt
Thomas_PC01.txt
Thomas_PC02.txt
Thomas_PC03.txt
Ziel:
Ordner PC01:
Berta_PC01.txt
Thomas_PC01.txt
Ordner PC02:
Berta_PC02.txt
Thomas_PC02.txt

Deine Lösung sortiert und verschiebt nach dem ersten Teil des Dateinamens.
Ich habe versucht das zu ändern, ohne Erfolg.
Danke und viele Grüße
Thomas
bastla
bastla 11.09.2016 um 13:05:49 Uhr
Goto Top
Hallo tomcruise!

Versuch es damit (vüllig ungetestet):
@echo off & setlocal
set "Ordner=D:\Dein Ordner"  
set "Separator=_"  

REM voruebergehend in den angegebenen Ordner wechseln
pushd "%Ordner%"  
REM in einer Schleife alle passenden Dateien (mit dem Separator im Namen; keine Ordner) abarbeiten
for /f "delims=" %%i in ('dir /b/a-d "*%Separator%*"') do (  
    REM 2. String isolieren
    for /f "tokens=2 delims=%Separator%" %%a in ("%%i") do (  
        REM Anzahl der mit dem String endendem Dateien ermitteln und
        REM wenn mehr als eine Datei vorhanden ist ...
        for /f %%n in ('dir "*%Separator%%%a.*"2^>nul^|findstr "Datei(en)"') do if %%n gtr 1 (  
            REM ... den Zielordner erstellen (Name des Ordners ohne Typ, zB ".txt") -  
            REM (falls bereits vorhanden, Fehlermeldung nicht anzeigen) ...
            md "%%~na" 2>nul  
            REM ... und alle entsprechenden Dateien in den Zielordner verschieben
            move "*%Separator%%%a" "%%~na">nul  
        )
    )
)
REM Wechsel des Ordners rückgängig machen
popd
Grüße
bastla
Tomilai
Tomilai 11.09.2016 um 16:43:52 Uhr
Goto Top
Hi Bastla,
es funktioniert wie gewünscht, vielen lieben Dank.
Hast du eine Empfehlung wo man Batchprogrammierung am besten lernen kann? Bücher?..
Ich hab gegooglet, aber das Thema scheint irgendwie nicht viel Literatur zu bieten.
Viele Grüße
Thomas
bastla
bastla 11.09.2016 um 17:25:22 Uhr
Goto Top
Hallo Tomcruise!

So ziemlich alles, was ich über Batch weiß, stammt aus dem Forum hier. face-smile

Ansonsten ist meine Empfehlung ganz klar "learning by doing" ...

Grüße
bastla
Tomilai
Tomilai 11.09.2016 um 21:57:04 Uhr
Goto Top
Danke dir Bastla face-smile
Schöne Grüße
Thomas
4sb3st
4sb3st 02.03.2018 um 15:31:49 Uhr
Goto Top
Batch mit Separator funktioniert wunderbar mit Dateien. Danke hier für die Mühe. Leider komme ich nicht weiter, aufgrund von Unwissenheit, aus diesem Script etwas zu basteln, dass auch nur für Ordner funktioniert.

Sprich: Alle Ordner samt Inhalt, bis zu einem bestimmten Separator in den Ordner legen, der nach dem Teil bis zum Separator benannt wird oder vllt. verständlicher face-smile

Guck im Ordnernamen bis zum Seperator, erstelle einen Ordner benannt nach dem Teil bis zum Separator und verschiebe Ordner inklusive Inhalt in den neuen Ordner. Wenn Ordner bereits vorhanden, dann verschiebe in den vorhandenen Ordner.

Beispiel:

(1)Ordner var1_var2_separator1_var3
verschieben in Ordner var1_var2

Ordner var1_var2_separator1_var4
verschieben in den bereits vorhandenen Ordner var1_var2

(2)Ordner var1_separator1_var2
verschieben in Ordner var1

usw.

Ich bedanke mich im Voraus für die Unterstützung. face-smile