Batchskript upgraden?
Ich habe ein Batch Skript geschrieben was eine Datei immer an eine andere Stelle kopiert und das alle 2 Sekunden. Jetzt soll die Datei aber nur kopiert werden wenn in der Datei auch was geändert wurde. Wie mache ich das?
Hier einmal mein Skript:
@echo off
set VonPfad="C:\Users\test\Desktop\test1\KopiertestServer.txt"
set NachPfad="C:\Users\test\Desktop\test2"
set ZeitIntervall="2"
:loop
xcopy /Y %VonPfad% %NachPfad%
timeout %ZeitIntervall%
goto loop
muss es Batch sein, oder würde auch Powershell gehen? (:
um ehrlich zu sein brauch ich sogar beides xD
1 Antwort
trivial:
demo.cmd
@echo off
set "VonPfad=%UserProfile%\Desktop\test1\KopiertestServer.txt"
set "NachPfad=%UserProfile%\Desktop\test2"
md "%NachPfad%" 2>nul &rem Zielordner erzeugen, falls dieser nicht existiert
set "ZeitIntervall=2"
:loop
for %%a in ("%VonPfad%") do (
rem %%~ta repräsentiert das datum der letzten Änderung!
if "%%~ta" neq "%lastChange%" (
copy "%%~fa" "%NachPfad%\" &rem der abschließende Backslash weist das Ziel als Ordner aus!
set "lastChange=%%~ta" &rem letze Änderung nächsten Vergleich merken...
) else (echo mache Nichts...) &rem der elsezweig ist nur zu demozwecken, kann weg
)
timeout %ZeitIntervall% >nul
goto :loop
pause
achte darauf, wie ich die "gänsefüßchen"(Quotes) gesetzt habe. Das ist wichtig!
Zum Kopieren von einzelnen Dateien arbeitet man nicht mit xcopy, sondern mit copy! (der Zielordner muss bei copy existieren und gegebenenfalls zuvor erzeugt werden)
PS: jetzt habe ich Zeit auch noch etwas effizienteres zu schreiben....
Die Batch und auch das Powershell-Script von guterfrager5 sind schrekliche Resorcenfresser, da diese immer und immerwieder die Gegebenheiten der Datei abfragen, was natürlich Unfug ist, folgendes Powershellscript klinkt sich passiv in die Ereignisverarbeitungskette und nur wenn das Ereignis Datei/Ordner "changed" vom System ausgelöst wurde , wird die definierte Action ausgelöst.
Das einzige, was Resourcen (Prozessorzeit) in Anspruch nimmt ist die Ausgabe eines Punktes alle 2 Sekunden. Ohne den Punkt könnte man auch die Schlafzyklen auf 10..100 Millisekunden setzen.
watchfile.ps1
$desktopFolder=[Environment]::GetFolderPath('Desktop')
$SourceFolder="$desktopFolder\test1" #ordner der überwacht werden soll
$FileFilter="KopiertestServer.txt" #datei(en) welche beobachtet werden
$global:DestinationFolder="$desktopFolder\test2" #wohin kopiert werden soll werden soll muss global deklariert werden, da action ein asnchroner Threat ist, und zur Laufzeit keinen Zugriff auf lokale Variablen hat!!!!
New-Item $global:DestinationFolder -ItemType "directory" -force #anlegen, wenn er nich existiert...
$watcher=New-Object System.IO.FileSystemWatcher
#zu beobachtender Ordner
$watcher.Path=$SourceFolder
#Dateimaske, welche Dateien beobchtet werden sollen
$watcher.Filter=$FileFilter
#auch Unterverzeichnisse beobachten?
$watcher.IncludeSubdirectories=$false
$watcher.EnableRaisingEvents=$false
### was soll getan werden (scriptblock variable)
$action={
# Rückgabe Datei, welche das Ereignis ausgelöst hat...
$path = $Event.SourceEventArgs.FullPath
#welche Veränderung der Auslöser war
$changeType = $Event.SourceEventArgs.ChangeType
#etwas anzeigen
Write-Host "$path was $changetype at $(get-date)"
Copy-Item $path $global:DestinationFolder -verbose
}
### registriere Ereignisse, auf welche wie reagiert werden soll
#du kannst natürlich für jedes Ereignis auch eine eigene Actionvariable definieren
#...oder einfach nur das Ereignis registrieren, auf welches Du reagieren möchtest
Register-ObjectEvent $watcher "Changed" -Action $action|Out-Null
#Reaktion auf andere Ereignisse für diese Demo deaktiviert
#Register-ObjectEvent $watcher "Created" -Action $action|Out-Null
#Register-ObjectEvent $watcher "Deleted" -Action $action|Out-Null
#Register-ObjectEvent $watcher "Renamed" -Action $action|Out-Null
#Endlosschleife. Welche das Script geöffnet hält, Reaktionszeit auf ereignisse 2 sekunden
while ($true) {
Write-Host '.' -n; #mache einfach einen punkt, damit man sieht, dass das Script noch lebt...
sleep 2
}
# siehe auch https://www.msxfaq.de/code/powershell/ps_und_callbackfunktion.htm
die anderen Ereignisse habe ich nur Auskommentiert, da diese für Deine Sache nicht relevant sind, aber durchaus informationswert haben.
Kann ich nicht nachvollziehen, ohne zu wissen wo der Syntax Fehler ausgelöst wird,
kommentiere das @Echo off am anfang aus:
::@echo off
Dann siehst Du Du wo der Syntaxfehler auftrat.
Falls es sich um einen Abbruchfehler handelt, starte cmd und rufe die Batch in der Console auf. Dann bekommt man auch kritische Fehler zu Gesicht.
hast du vielleicht was an den klammern verändert? oder das unnütze Zeug nicht gründlich weggeputzt?
demo.cmd
@echo off
set "VonPfad=%UserProfile%\Desktop\test1\KopiertestServer.txt"
set "NachPfad=%UserProfile%\Desktop\test2"
md "%NachPfad%" 2>nul
set "ZeitIntervall=2"
:loop
for %%a in ("%VonPfad%") do (
if "%%~ta" neq "%lastChange%" (
copy "%%~fa" "%NachPfad%\"
set "lastChange=%%~ta"
)
)
timeout %ZeitIntervall% >nul
goto :loop
pause
der häufigste Grund für Syntaxfehler sind zuwenige/zuviele Klammern und nicht abgeschlossene "Quotes".
ok funktioniert jetzt. Aber erkannt wird ja nur eine Änderung wenn sich eine Minute ändert was ja nicht sein sollte. Eigentlich soll ja jede 2. Sekunde geprüft werden. Also ja er prüft jede 2. Sekunde aber erkennen tut er es nur wenn eine Minute dazwischen liegt. Ich hoffe man versteht mich. ^^
sorry hatte echt nicht mehr auf dem Schirm, das Batch keine Sekunden im %~t.-Parameter liefert.
Ja dann wirds für Batch eng...WMIC datafile ... liefert genaue Zeitstempel :
@echo off
set "VonPfad=%UserProfile%\Desktop\test1\KopiertestServer.txt"
set "NachPfad=%UserProfile%\Desktop\test2"
md "%NachPfad%" 2>nul
set "ZeitIntervall=2"
:loop
rem WMIC erwartet Pfade im Javaformat . (Backslashes verdoppeln)
for /f "tokens=2,3 delims=,+" %%a in ('wmic datafile where "Name='%VonPfad:\=\\%'" get Caption^,LastModified /format:csv^|find "%ComputerName%"') do (
if "%%~b" neq "%lastChange%" (
echo LastModified: %%b Caption: %%a
copy "%%a" "%NachPfad%\"
set "lastChange=%%b"
)
)
timeout %ZeitIntervall% >nul
goto :loop
pause
Aber nicht zu früh freuen:
In einigen Windows11 Devs wurde wmic bereits entfernt, ergo funktioniert letztere Lösung nicht mehr immer und überall und ewig.
...bleibt die Verwendung Powershell in der Batch:
...
for /f %%a in ('powershell "(gi '%VonPfad%').LastWriteTime.ToString('yyyyMMddhhmmss.ffff')"') do (
if "%%a" neq "%lastChange%" (
copy "%VonPfad%" "%NachPfad%\"
set "lastChange=%%a"
)
)
...
...aber wenn man schon Powershell verwendet, warum dann nicht gleich auf Powershell umsteigen?
Zeit für Dich Batch weitgehend den Rücken zu kehren! 👎🙌oben habe ich ein sehr effiziente PoshScript gepostet.
also wenn ich den teil ausprobiere:
bekomme ich immer ein Systax error