o0julia0o
Goto Top

Batch soll 2 Dateien suchen, wenn vorhanden die gefundene aus einem Ordner kopieren

hi, ich bastel hier schon ein paar Tage dran herum, also immer mal wieder. So ganz funktioniert das nie, wie ich das möchte.

Strukur ist folgende:
Batch-Ort: Hauptordner\#Im
Hauptordner\#Im\GB\
dort sind 2 Dateien enthalten: wasser.txt & wasser2.txt und noch weitere Dateien und Ordner.

Der Gesamte Hauptordner samt Unterordnern außer dem #Im-Ordner soll nun nach wasser.txt & wasser2.txt durchsucht werden.

1. Es soll wenn wasser.txt gefunden wird der Inhalt des GB-Ordners kopiert werden in den Ordner mit der wasser.txt. Dabei können mehrere Ordner eine wasser.txt enthalten. ->gleiches gilt für die wasser2.txt

2. Zuvor soll aber die wasser.txt als wasser.txt_sic gesichert werden, die in dem jeweiligen Ordner gefunden wurde. Wenn bereits eine wasser.txt_sic vorhanden ist soll sie als wasser.txt_sic2 gesichert werden. Also wenn ich die batch 5x ausführe, dann wird in Zukunft die gefundene wasser.txt immer als wasser.txt_sic2 gesichert und die wasser.txt_sic nur beim 1. Mal verwendet als Sicherungsname. -> gleiches gilt für die wasser2.txt

3. Soweit hatte ich das alles schon hinbekommen. Jetzt kommt allerdings eine Einschränkung für Schritt 1. Hier soll der gesamte Ordner außer einer der beiden Dateien wasser.txt und wasser2.txt, nämlich die nicht gefunden wurde kopiert werden. Also am Beispiel:

Hauptordner\Daten1\wasser2.txt wird gefunden
Hauptordner\Daten1\wasser.txt existiert nicht

Jetzt soll die gefundene wasser2.txt als wasser2.txt_sic2 gespeichert werden, da dort auch eine wasser2.txt_sic bereits vorhanden ist.

Danach soll der Ordnerinhalt #Im\GB in den gefundenen Ordner: Hauptordner\Daten1 kopiert werden. Aber Achtung! Nicht die wasser.txt, weil diese nicht in dem Ordner existiert in dem Beispiel.

Das ist mein bester Versuch, irgendwo hapert es aber:
@echo off
setlocal enabledelayedexpansion

set "sourceDir=%~dp0GB"    
set "searchFile1=wasser.txt"  
set "searchFile2=wasser2.txt"  

REM Gehe alle Ordner ein Verzeichnis höher als #Im durch
for /f "delims=" %%A in ('dir /s /b /a-d "%~dp0..\%searchFile1%" ^| find /v "%~dp0"') do (  
    set "dir=%%~dpA"  

    REM Überprüfen, ob wasser.txt existiert
    if exist "%%A" (  
        REM Wenn die _sic-Datei existiert, erstelle _sic2 (Überschreiben)
        if exist "!dir!wasser.txt_sic" (  
            copy "%%A" "!dir!wasser.txt_sic2" /Y  
        ) else (
            REM Wenn die _sic-Datei nicht existiert, erstelle sie
            copy "%%A" "!dir!wasser.txt_sic" /Y  
        )
    )

    REM Überprüfen, ob wasser2.txt existiert
    if exist "%%A" (  
        REM Wenn die _sic-Datei existiert, erstelle _sic2 (Überschreiben)
        if exist "!dir!wasser2.txt_sic" (  
            copy "%%A" "!dir!wasser2.txt_sic2" /Y  
        ) else (
            REM Wenn die _sic-Datei nicht existiert, erstelle sie
            copy "%%A" "!dir!wasser2.txt_sic" /Y  
        )
    )

    REM Kopieren des GB-Ordners nach !dir!
    REM Überprüfen, ob die Dateien wasser.txt oder wasser2.txt vorhanden sind
    if exist "!dir!wasser.txt" (  
        xcopy /E /I /Y "%sourceDir%\*" "!dir!" /EXCLUDE:wasser2.txt >nul 2>&1  
    ) 
    if exist "!dir!wasser2.txt" (  
        xcopy /E /I /Y "%sourceDir%\*" "!dir!" /EXCLUDE:wasser.txt >nul 2>&1  
    )
)

pause
exit

Ich hoffe Jemand kann da etwas Licht ins Dunkel bringen. Danke schon einmal!

Content-ID: 671723

Url: https://administrator.de/forum/batch-soll-2-dateien-suchen-wenn-vorhanden-die-gefundene-aus-einem-ordner-kopieren-671723.html

Ausgedruckt am: 07.04.2025 um 03:04 Uhr

DivideByZero
DivideByZero 03.03.2025 um 21:34:37 Uhr
Goto Top
Es ist wirklich Zeit für Powershell. Und solche Fragen kannst Du übrigens heutzutage hervorragend im Dialog mit ChatGPT oder Claude lösen.
o0Julia0o
o0Julia0o 03.03.2025 um 21:42:05 Uhr
Goto Top
Jo, danke ich hab von Powershell so gar keine Ahnung. Einmal ein Tutroial gemacht von 30 Minuten ;). Soll schon per Batch sein. Ist ja auch theoretisch möglich. Einzelne parts bekomme ich hin, aber im Gesamtfluss hapert es noch.
TK1987
Lösung TK1987 04.03.2025 aktualisiert um 09:45:06 Uhr
Goto Top
Moin Julia,

Zitat von @o0Julia0o:
    REM Überprüfen, ob wasser.txt existiert
    if exist "%%A" (  
nach der Wasser.txt hast du ja in der For-Schleife bereits gesucht, diese existiert an der Stelle also immer und die Überprüfung ist somit überflüssig.

    REM Überprüfen, ob wasser2.txt existiert
    if exist "%%A" (   
verstehe ich an der Stelle nicht. %%A ist hier ja die Wasser.txt und nicht die Wasser2.txt. Du müsstest die Schleife ein weiteres mal starten und diesmal statt der Wasser.txt nach der Wasser2.txt suchen.

Um den Code nicht doppelt schreiben zu müssen, verwendest du dann am besten eine Funktion und übergibst die Dateinamen an diese. Zudem zum Kopieren besser Robocopy statt Xcopy nutzen.
@echo off
setlocal EnableDelayedExpansion

set "sourceDir=%~dp0GB"      

REM              <filename>    <excludeFilename>
call :searchFile "wasser.txt"  "wasser2.txt"  
call :searchFile "wasser2.txt" "wasser.txt"  

pause

exit /b 0
:searchFile <filename> <excludeFilename>
  REM Gehe alle Ordner ein Verzeichnis höher als #Im durch
  for /f "delims=" %%A in ('dir /s /b /a-d "%~dp0..\%~1" ^| find /v "%~dp0"') do (  
    set "dir=%%~dpA\"  
 
    REM Wenn die _sic-Datei existiert, erstelle _sic2 (Überschreiben)
    if exist "!dir!%~1_sic" (  
      copy "%%A" "!dir!%~1_sic2" /Y    
    ) else (
      REM Wenn die _sic-Datei nicht existiert, erstelle sie
      copy "%%A" "!dir!%~1_sic" /Y    
    )

    REM Kopieren des GB-Ordners nach !dir!, <excludeFilename> ausschließen
    >nul 2>&1 robocopy "%sourceDir%" "!dir!" /E /XF "%~2"  
  )
  REM Ende der Funktion
  exit /b 0

Jo, danke ich hab von Powershell so gar keine Ahnung.
Eigentlich ist die Powershell-Syntax viel leicht verständlicher als die von cmd. Gerade wenn das Vorhaben mal etwas Umfangreicher wird, wird Batch auch für das geübte Auge schnell sehr unübersichtlich - von den ganzen Fallstricken und Bugs mal garnicht zu sprechen.

Hier das Ganze mal alternativ iin Powershell, die Wahl liegt bei dir...
$sourceDir   = "$PsScriptRoot\GB"  
$rootDir     = (Get-Item $PsScriptRoot).Parent.FullName
$searchFiles = @(
  "Wasser.txt"  
  "Wasser2.txt"  
)

# Suche im Rootverzeichnis nach $searchFiles (Skriptverzeichnis ausgenommen)
foreach ($file in Get-ChildItem -Recurse -File -Path $rootDir -Include $searchFiles | Where FullName -NotLike "$PsScriptRoot\*") {  
  
  # Wenn _sic existiert, _sic2 erstellen bzw. überschreiben. Sonst _sic erstellen 
  if (Test-Path ($file.FullName + "_sic")) { copy -Path $file -Destination ($file.FullName + "_sic2") }  
  else { copy -Path $file -Destination ($file.FullName + "_sic") }  
  
  # Kopiere Daten aus Quellordner, schließe alle Dateinamen aus $searchFiles ausser der gefundenen Datei selbst aus.
  copy -Recurse -Path "$sourceDir\*" -Destination $file.DirectoryName -Exclude ($searchFiles -ne $file.Name)  
  
}

Gruß Thomas
o0Julia0o
o0Julia0o 06.03.2025 um 17:36:02 Uhr
Goto Top
Danke Dir Thomas! funktionieren tun beide Codes!

Mal kurz ps1 und Batch miteinander verglichen:

ps1:
64 Wörter
474 Zeichen (ohne Leerzeichen)

batch:
80 Wörter
482 Zeichen (ohne Leerzeichen)

Wobei ich denke, dass für nicht so Clevere Batch eher geeignet ist, da es eher sich an der gesprochenen Sprache orientiert. Man beschreibt da ausführlicher, was getan werden soll und braucht nicht für jeden Befehl wieder neue Wörter sondern kombiniert einfach 2 verschiedene Wörter und hat dann einen neuen Befehl.

ps1 muss aber aufwändig gestartet werden ohne weitere Dateien(ps1 funkioniert nach Umwandlung zur exe nicht mehr(Batch aber auch nicht))
Batch reicht ein Doppelklick zum Start

ps1 Ausführungszeit im Testornder: 6,5 Sekunden
Batch Ausführungszeit im Testordner: 15 Sekunden
(bei sehr kleinen Datenmengen ist die Batch aber etwas schneller, dann dauert die Ausführung aber ohnehin nur <3 Sekunden)

Wobei die Ausführungszeit ja auch ermogelt ist, denn die Batch lässt 2x den gesamten Ordner durchsuchen. Die ps1 nur einmal. Geht das mit der Batch nicht auch effektiver?
TK1987
TK1987 06.03.2025 aktualisiert um 19:11:02 Uhr
Goto Top
Zitat von @o0Julia0o:
Mal kurz ps1 und Batch miteinander verglichen:

ps1:
64 Wörter
474 Zeichen (ohne Leerzeichen)
Hierzu sei gesagt, dass ich dieses Powershell-Skript, weil Anfängergerechter, sehr ausführlich geschrieben habe.

Man kann da...

  • einige Parameter einfach weglassen ("-Path" z.B. fast immer)
  • andere einfach kürzen (z.B. statt "-Recurse" einfach "-R")
  • viele Befehle durch kurze Aliase ersetzen (z.B. statt "Get-ChildItem" einfach "gci", "ls" oder "dir").

Das geht also auch noch mit deutlich weniger Wörtern und Zeichen.

Wobei ich denke, dass für nicht so Clevere Batch eher geeignet ist, da es eher sich an der gesprochenen Sprache orientiert.
Das sehe ich genau anders herum.

Wer noch nie mit sowas gearbeitet hat, kann den Powershell Code vom reinen lesen eher verstehen.

Bei Batch wird der Code durch die vielen Prozentzeichen und Parameter, deren Bedeutung man erst kennen muss, schnell abstrakt.

ps1 muss aber aufwändig gestartet werden
Geht doch mittlerweile leicht über das Kontextmenü (Rechtsklick > Mit Powershell ausführen). Aber zugegeben, Doppelklick ist einfacher - das geht hier nur mittels Verknüpfung.

ps1 funkioniert nach Umwandlung zur exe nicht mehr(Batch aber auch nicht))
Doch, Powershell Skripte funktionieren als exe umgewandelt.

Geht das mit der Batch nicht auch effektiver?
Leider nein.
DivideByZero
DivideByZero 06.03.2025 um 21:12:44 Uhr
Goto Top
Wobei ich denke, dass für nicht so Clevere Batch eher geeignet ist, da es eher sich an der gesprochenen Sprache orientiert.
Das sehe ich genau anders herum.
Ebenso, denn Batch ist ja im Wesentlichen ein Stapel von externen Kommandos (da kommt es her), die ggf. auch mit ihren Parametern besonders kryptisch sind (z.B. find). Powershell dagegen als Programmiersprache sagt viel mehr, was es gerade macht und ist stringenter, weil es auf einen größeren eigenen Befehlssatz zurückgreift. Auch eingebundene Module sind da lesbarer.
o0Julia0o
o0Julia0o 07.03.2025 aktualisiert um 00:16:15 Uhr
Goto Top
Doch, Powershell Skripte funktionieren als exe umgewandelt.

Aber nicht deine hier. Das kommt wohl wegen relativen Pfaden. Habe eine exe erstellt, die funktionierte nicht. Aber wie gesagt, bei der Batch ist da das gleiche Problem.

Dann muss man aber auch mehr Vokabeln können bei Powershell, damit man die Sprache besser verstehen kann als bei der Batch. Dort reichen weniger Vokabeln um mehr machen zu können, sie richtig kombinierend. Ich habe mal C++ gemacht ne Weile. Aber manche Gehirne sind vielleicht einfach weniger für Programmiersprachen ausgelegt als für Batch. Ich könnte nicht einmal mehr die Welt begrüßen. OReily oder so, das habe ich behalten, aber das ist kein Befehl ;). Batch geht mir mehr ins Hirn. Und ja, ich bin kein Held. Aber davon rede ich ja. Es ist einfacher für einfache Leute. Das Powerpoint viel mehr kann, darüber müssen wir nicht reden.


Geht das mit der Batch nicht auch effektiver?
Leider nein.
Dachte, dass es eine Suche gibt mit ner Forschleife, wo man nach wasser.txt und wasser2.txt gleichzeitig suchen lässt. Ich hatte dann mit "set" herumprobiert. So würde man dann halt das 2te Durchkämmen der Ordner umgehen.

P.s.
cout hello world?;
vielleicht immerhin das doch noch eingefallen ;)
TK1987
TK1987 07.03.2025 aktualisiert um 08:20:53 Uhr
Goto Top
Zitat von @o0Julia0o:
Aber nicht deine hier
Das ist richtig. Das Problem ist, dass Skriptbezogene Variablen (wie hier "PsScriptRoot") in der exe nicht vorhanden sind.
Je nachdem, ob das Skript über Powershell oder als exe umgewandelt gestartet wird, ändert sich auch die Verfahrensweise, um den Pfad zu ermitteln, wie hier beschrieben und gelöst.

Dachte, dass es eine Suche gibt mit ner Forschleife, wo man nach wasser.txt und wasser2.txt gleichzeitig suchen lässt.
Das ginge theoretisch auch, würde aber die Effizienz i.d.R. eher verringern als steigern. Du musst in dem Fall ja innerhalb der Schleife wieder prüfen, welche der beiden Dateien gefunden wurde, um die jeweils andere auszuschließen - und if-Verzweigungen sind bei Batch leider ziemliche Zeitfresser, weswegen man mit 2 Durchläufen normaler Weise besser dran ist.

Darfst es aber gerne mal wie folgt versuchen:
@echo off
setlocal enabledelayedexpansion

set "sourceDir=%~dp0GB"  
set "searchFile1=wasser.txt"  
set "searchFile2=wasser2.txt"  

for /f "delims=" %%A in ('dir /s /b /a-d "%~dp0..\%searchFile1%" "%~dp0..\%searchFile2%" ^| find /v "%~dp0"') do (    
  set "dir=%%~dpA"  
  if /i "%%~nxA" EQU "%searchFile1%" (set "exclude=%searchFile2%") else (set "exclude=%searchFile1%")  

  REM Wenn die _sic-Datei existiert, erstelle _sic2 (Überschreiben)
  if exist "!dir!%%~nxA_sic" (    
    copy "%%A" "!dir!%%~nxA_sic2" /Y      
  ) else (
    REM Wenn die _sic-Datei nicht existiert, erstelle sie
    copy "%%A" "!dir!%%~nxA_sic" /Y      
  )

  REM Kopieren des GB-Ordners nach !dir!, <excludeFilename> ausschließen
  >nul 2>&1 robocopy "%sourceDir%" "!dir!" /E /XF "!exclude!"  
)

pause
o0Julia0o
o0Julia0o 10.03.2025 um 19:14:13 Uhr
Goto Top
Danke, aber da läuft was schief mit der Batch. Der sucht überall. Auch im #Im-Ordner(und höher), da wo die Batch liegt.
TK1987
TK1987 11.03.2025 um 07:24:44 Uhr
Goto Top
Zitat von @o0Julia0o:
Danke, aber da läuft was schief mit der Batch.
Hier im Test funktioniert es einwandfrei. Ist ja eigentlcih auch der selbe Code wie bei der anderen Batch.

Der sucht überall. Auch im #Im-Ordner
Da soll er doch auch suchen?!

(und höher), da wo die Batch liegt.
Die Batch liegt doch ein Verzeichnis tiefer als der "#Im"-Ordner, oder? Nur deswegen suchst du doch mit "%~dp0..". Im Skriptverzeichnis selbst kann er eigentlich nicht suchen, ist durch "find /v" ja nach wie vor ausgeschlossen.

Ersetze mal bitte die Befehle innerhalb der For-Schleife einfach mit einem
echo %%A
Wie sieht die Ausgabe aus?
o0Julia0o
o0Julia0o 12.03.2025 um 10:58:43 Uhr
Goto Top
Zitat von @TK1987:
Der sucht überall. Auch im #Im-Ordner
Da soll er doch auch suchen?!
Nein, das ist ja in der Lösung auch nicht so. Der #Im-Ordner bleibt unangetastet. Lediglich die Quelledateien sind im #Im-Ordner, also #Im\GB\

Ersetze mal bitte die Befehle innerhalb der For-Schleife einfach mit einem
echo %%A
Wie sieht die Ausgabe aus?
%A
TK1987
TK1987 12.03.2025 aktualisiert um 11:35:05 Uhr
Goto Top
Zitat von @o0Julia0o:
Ersetze mal bitte die Befehle innerhalb der For-Schleife einfach mit einem
echo %%A
Wie sieht die Ausgabe aus?
%A
Natürlich sollst du den Satz der For-Schleife nicht mit abändern 🤣. Nur die auszuführenden Befehle der For-Schleife ändern, also...
for /f "delims=" %%A in ('dir /s /b /a-d "%~dp0..\%searchFile1%" "%~dp0..\%searchFile2%" ^| find /v "%~dp0"') do (  
  echo %%A
)
o0Julia0o
o0Julia0o 13.03.2025 um 16:51:57 Uhr
Goto Top
Oh, da hatte ich wohl was falsch. Das Ergebnis sieht folgend aus:

E:\Temp\GB\Cul\Data\ug\wasser.txt
E:\Temp\GB\Cul\Data\ug\wasser2.txt
E:\Temp\GB\Cul\Data\ug\89\wasser.txt
E:\Temp\GB\Cul\Data\ug\89\wasser2.txt
Das System kann die angegebene Datei nicht finden.

-Es wird also korrekterweise nicht im #Im-Ordner gesucht
-Es wird auch korrekt gesichert
-Es wird aber nichts kopiert

Die Meldung: "Das System kann die angegebene Datei nicht finden" kommt allerdings bereits vor dem Robocopy-Befehl. Wenn ich ein
pause
echo jetzt kommt Robocopy
vor dem Robocopy-Befehl einfüge, sieht das Ergebnis so aus:

E:\Temp\GB\Cul\Data\ug\wasser.txt
E:\Temp\GB\Cul\Data\ug\wasser2.txt
E:\Temp\GB\Cul\Data\ug\89\wasser.txt
E:\Temp\GB\Cul\Data\ug\89\wasser2.txt
Das System kann die angegebene Datei nicht finden.
Drücken Sie eine beliebige Taste . . .
jetzt kommt Robocopy:
Drücken Sie eine beliebige Taste . . .

Und in dem Ornder E:\Temp\GB\Cul\#Im\GB befinden sich sowohl eine wasser.txt und einer wasser2.txt.

Diese codezeile macht wohl das Problem:
copy "%%A" "!dir!%%~nxA_sic2" /Y   
Müsste nicht aus dem sourcedir Verzeichnis kopiert werden?
TK1987
TK1987 14.03.2025 um 11:26:10 Uhr
Goto Top
Zitat von @o0Julia0o:
- Es wird aber nichts kopiert
Gar nichts? Poste doch mal bitte die Robocopy Ausgabe.
Beachte, dass robocopy nur kopiert, wenn Quelle- und Ziel unterschiedlich sind. Wenn die Dateien also im Ziel bereits existieren und identisch sind, kann das durchaus richtig sein, dass nichts kopiert wird.

Diese codezeile macht wohl das Problem:
copy "%%A" "!dir!%%~nxA_sic2" /Y   
Müsste nicht aus dem sourcedir Verzeichnis kopiert werden?
Die Zeile stammt doch aus deinem Code - du musst doch selbst wissen, was du da tun willst?!

Laut deiner Eingangsbeschreibung müsste das aber schon richtig so sein...
2. Zuvor soll aber die wasser.txt als wasser.txt_sic gesichert werden, die in dem jeweiligen Ordner gefunden wurde.

Die Zeile kann den Fehler aber eigentlich nicht produzieren. Die Datei muss ja zwingend existierens (wurde bei dem dir-Befehl ja gefunden).

Ob wirklich diese Zeile den Fehler verursacht, kannst du aber leicht prüfen, indem du für die Zeile die Befehlsausgabe wieder einschaltest. D.h.
@echo on
copy "%%A" "!dir!%%~nxA_sic2" /Y  
@echo off
o0Julia0o
o0Julia0o 17.03.2025 um 15:51:59 Uhr
Goto Top
Daran scheint es zu liegen, denn mit echo on erhalte ich:
"Das System kann die angegebene Datei nicht finden."

Es wird gar nichts kopiert bei Robocopy - Robocopy macht aus keine Ausgabe. Auch nicht mit echo on. Dort wird dann halt nur der Befehl noch mal in der Kommandozeile ausgegeben. Aber sonst nichts.

So sieht mein Testaufbau aus (im Ordner #Im befindet sich die Batch):
batch
TK1987
TK1987 17.03.2025 um 16:15:10 Uhr
Goto Top
Zitat von @o0Julia0o:
Robocopy macht aus keine Ausgabe.
Doch, aber die haben wir aber nach nul umgeleitet und somit gelöscht face-wink

Das Problem ist nicht der Copy-Befehl, sondern der abschließende Backslash im Zielpfad "!dir!" bei robocopy. Dieser muss dann verdoppelt werden, damit es funktioniert:
  >nul 2>&1 robocopy "%sourceDir%" "!dir!\" /E /XF "!exclude!"   
o0Julia0o
o0Julia0o 29.03.2025 um 16:42:14 Uhr
Goto Top
Oh jo. Danke Dir TK1987! Von der Geschwindigkeit merkt man es, aber ich müsste schon übertrieben große Ordner erstellen, damit ich da jetzt ne Sekunde unterschied hinbekomme. Warum neulig die batch 15 Sekunden dauerte, ist mir schleierhaft ;) War der gleiche Testornder mit weniger Inhalt. Vielleicht lief noch irgendetwas anderes im Hintergrund.