Robocopy mit Powershell und Fortschrittsanzeige
Die Aufgabe
Ich sichere von Zeit zu Zeit verschiedene Verzeichnisse von unserem Familien-Server auf mehrere externe Festplatten weg.
In Powershell war das schnell eingerichtet:
Dabei wird in der Datei copyjob.txt je das zu sichernde Verzeichnis angegeben. Dieses wird dann vom Server auf die Platte gesichert.
PRO
So entschied ich mich dazu, es etwas komplexer, in der Ausgabe aber optisch schöner ...
und in den Logs etwas nachvollziehbarer zu machen.
A = Kurzbeschreibung der Platte
B = Sicherungsdatum
C = Quelle
D = Anzahl zu kopierende Dateien
E = Größe aller Dateien in Bytes
F = Ziel
G = Anzahl zu kopierende Dateien
H = Größe aller Dateien in Bytes
Ist ab D der Rest leer, war die Sicherung aktuell und es musste nichts neues kopiert oder gelöscht werden.
Funktionsweise
Powershell - Code
Mein Fazit
Erweiterung
Gruß Sam
Ich sichere von Zeit zu Zeit verschiedene Verzeichnisse von unserem Familien-Server auf mehrere externe Festplatten weg.
In Powershell war das schnell eingerichtet:
get-content ((Get-Location).path + "Copyjob.txt") | ForEach-Object { robocopy "L:\$($_)" "$((get-location).path)$($_)" /mir /r:0 /w:0 /tee /np /log:"$(get-date -uformat "%Y-%m-%d")_Logfile.txt" }
PRO
- Einfach
- Schnell
- Unübersichtlich in der Ausgabe
- Keine Vergleichbarkeit zwischen den Sicherungsständen auf den Platten
- Copyjob.txt liegt auf der Sicherungsplatte, dadurch ist es bei mir schon mal zu unterschiedlichen Sicherungen gekommen.
So entschied ich mich dazu, es etwas komplexer, in der Ausgabe aber optisch schöner ...
und in den Logs etwas nachvollziehbarer zu machen.
A = Kurzbeschreibung der Platte
B = Sicherungsdatum
C = Quelle
D = Anzahl zu kopierende Dateien
E = Größe aller Dateien in Bytes
F = Ziel
G = Anzahl zu kopierende Dateien
H = Größe aller Dateien in Bytes
Ist ab D der Rest leer, war die Sicherung aktuell und es musste nichts neues kopiert oder gelöscht werden.
Funktionsweise
- Ermitteln des Laufwerksbuchstabens des Sicherungslaufwerks anhand hinterlegter Datenträgerseriennummern.
- Robocopy im "Flugmodus" /L ausführen
- Logfile einlesen und Dateien zählen sowie Datenvolumen berechnen
- Robocopy im "Flugmodus" /L ausführen um ein "schönes" "lesbares" Log zu schreiben
- Robocopy ausführen
- Parallel in einer Schleife das Log einlesen und Dateien zählen sowie Datenvolumen berechen.
- Anhand dessen und der Daten aus dem ersten Flugmodus-Lauf wird die Differenz berechnet und eine Progressbar angesteuert
- Während des Kopiervorgangs wird eine Progressbar angezeigt. Hier stehen Informationen zu Dateianzahlen, Datenvolumen und Fortschritt in %
- Sicherungsvorgang in Superlog (.csv-Datei) auf dem Server wegschreiben um eine Übersicht über alle Sicherungen auf allen Platten zu bekommen
Powershell - Code
cls
# Konfiguration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Array für Seriennummern möglicher Sicherungsplatten. Hierrüber wird der Laufwerksbuchstabe des Sicherungsdatenträgers bestimmt.
# Wird kein Datenträger mit passender Seriennummer gefunden, wird eine Fehlermeldung ausgegeben.
$HDDSerials = @()
$HDDSerials += 123456789
$HDDSerials += -123456789
# Array für Beschreibungen der Sicherungsdatenträger (wird in der Konsole angezeigt)
$HDDLong = @()
$HDDLong += 'Western Digital Black 1TB vollverschlüsselt'
$HDDLong += 'Western Digital Purple 3TB vollverschlüsselt'
# Array für Kurzbeschreibungen der Sicherungsdatenträger (wird im Superlog angezeigt)
$HDDShort = @()
$HDDShort += 'WD_Black_1TB'
$HDDShort += 'WD_Purple_3TB'
# Parameter für Robocopy und Unterfunktionen
$RC_Paramset_Shadow = '/MIR /R:0 /W:0 /FP /NP /NC /NDL /NJH /NJS /BYTES' # Ermittlung des Kopieraufwandes und eigentlicher Kopiervorgang
$RC_Paramset_Log = '/MIR /R:0 /W:0 /FP /NP /BYTES' # Erstellung des "lesbaren" Logs
$RC_LogSearch = '(?<=\s+)\d+(?=\s+)' # RegEx zum extrahieren der Dateigrößen aus den ShadowLogs
$RC_Superlog = 'L:\00 Software\99 Sicherungen\Sicherungsstatus.csv' # Superlog: Hier werden die Summarys aller Sicherungen als .csv gesichert
# Array der zu sichernden Pfade. Auf dem Sicherungslaufwerk wird aus dem Pfad der jeweils letzte Ordner als Ziel gesetzt
# Beispiel: '\\Server\Freigabe\Ordner 1' wird zu 'X:\Ordner 1'
$Sicherungspfade = @()
$Sicherungspfade += '\\Server\Daten\00 Software'
$Sicherungspfade += '\\Server\Daten\01 Dokumente'
$Sicherungspfade += '\\Server\Daten\02 Geocaching'
$Sicherungspfade += '\\Server\Daten\03 Bilder'
$Sicherungspfade += '\\Server\Daten\05 Musik'
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Ziel-HDD ermitteln anhand der Seriennummer des Datenträgers
$ZielHDD = $null
Write-Host -ForegroundColor white "Ermittle Sicherungslaufwerk ... "
$FSO = New-Object -com Scripting.FileSystemObject
$FSO.Drives | foreach {
$Drive = $_
if($ZielHDD -eq $null) {
$i = 0
$HDDSerials | foreach {
if($_ -eq ($FSO.GetDrive($Drive.path).SerialNumber)) {
$ZielHDD = $Drive.path
$Platte = $HDDShort[$i]
Write-Host -ForegroundColor Gray " - Laufwerk: " -NoNewline
write-host -ForegroundColor green "$($Drive.path)\"
write-host -ForegroundColor gray " - Beschreibung: " -NoNewline
write-host -ForegroundColor green $HDDLong[$i]
}
$i++
}
}
}
# Keine Ziel-HDD gefunden? Abbruch!
if($ZielHDD -eq $null) {
write-host -ForegroundColor red " ... Es konnte kein Sicherungslaufwerk gefunden werden!"
write-host -ForegroundColor red " ... Abbruch."
write-host ''
break
} else {
$TempDatei = "$ZielHDD\copyjob.tmp"
}
# Robocopy
write-host ''
$Sicherungspfade | Foreach {
$Sicherung = $_
$LogDatei = "$ZielHDD\$(get-date -uformat "%Y-%m-%d")"
# Zielverzeichnis aus Quelle ermitteln
$ZielVerzeichnis = $_.split("\")
$ZielVerzeichnis = $Zielverzeichnis[$Zielverzeichnis.length-1]
# Ausgabe
write-host -ForegroundColor White "Sicherung von " -NoNewline
write-host -ForegroundColor yellow $Sicherung -NoNewline
write-host -ForegroundColor white " nach " -NoNewline
write-host -ForegroundColor yellow "$ZielHDD\$ZielVerzeichnis"
write-host -ForegroundColor gray " - Ermittle Kopieraufwand ..."
# Robocopy Shadowmode
$Args = '"{0}" "{1}" /L {2} /LOG:"{3}"' -f $_, "$ZielHDD\$ZielVerzeichnis", $RC_Paramset_Shadow, $TempDatei
Start-Process -Wait -FilePath robocopy -ArgumentList $Args -WindowStyle Hidden
# Logdatei einlesen
$Staging = Get-Content -Path "$TempDatei"
# Dateien (Zeilen) zählen
$FileCount = $Staging.count -1
if($FileCount -ne 0) {
# Dateigrößen (Bytes) addieren
[LONG]$BytesTotal = 0
[RegEx]::Matches(($Staging -join "`n"), $RC_LogSearch) | % { $BytesTotal = 0 } { $BytesTotal += $_.Value }
# Ausgabe
write-host -ForegroundColor gray " ... $filecount Dateien"
Write-host -ForegroundColor gray " ... $([math]::round(($BytesTotal/1024/1024/1024),3)) GB"
# Temp-Datei löschen
Remove-Item $Tempdatei -Force
# Finales Logfile bestimmen
$LogDatei = "$LogDatei - $ZielVerzeichnis.txt"
#Richtiges Log erstellen
write-host -ForegroundColor Gray " - Erstelle lesbares Log ..."
$Args = '"{0}" "{1}" /L {2} /LOG:"{3}"' -f $_, "$ZielHDD\$ZielVerzeichnis", $RC_Paramset_Log, $LogDatei
Start-Process -Wait -FilePath robocopy -ArgumentList $Args -WindowStyle Hidden
write-host -ForegroundColor Gray " ... Log erstellt"
# Robocopy im Originalmodus
Write-Host -ForegroundColor gray " - Sicherung läuft ..."
$Args = '"{0}" "{1}" {2} /LOG:"{3}"' -f $_, "$ZielHDD\$ZielVerzeichnis", $RC_Paramset_Shadow, $TempDatei
$RC = Start-Process -FilePath robocopy -ArgumentList $Args -WindowStyle Hidden -PassThru
Start-Sleep -Milliseconds 500
# Prozessfortschritt anzeigen
while (!$RC.HasExited) {
Start-Sleep -Milliseconds 500
[LONG]$BytesCopied = 0
$Staging = Get-Content -Path $TempDatei
if(($Staging.count) -ne 0 ) { # Bei großen Verzeichnissen und wenig Änderungen kann es zu lange dauern bis ein Datensatz im Log steht. Das führt zu einer Fehlermeldung. Daher dieser Workarround.
$BytesCopied = [Regex]::Matches($Staging, $RC_LogSearch) | ForEach-Object -Process { $BytesCopied += $_.Value; } -End { $BytesCopied }
if($BytesTotal -ne 0 ) {
Write-Progress -Activity "Sicherung von ""$Sicherung"" nach ""$ZielVerzeichnis""" -Status "$($Staging.Count -1) von $FileCount Dateien bzw. $([math]::round($BytesCopied/1024/1024/1024,3)) GB von $([math]::round($BytesTotal/1024/1024/1024,3)) GB kopiert.." -PercentComplete (($BytesCopied/$BytesTotal)*100)
}
}
}
write-progress -Activity "Sicherung von ""$Sicherung"" nach ""$ZielVerzeichnis""" -Completed
# Temp-Datei löschen
Remove-Item $Tempdatei -Force
# Ausgabe
write-host -ForegroundColor gray " - Sicherung abgeschlossen: " -NoNewline
if([math]::round(($BytesCopied/$BytesTotal)*100,0) -eq 100) {
Write-Host -ForegroundColor green "$([math]::round(($BytesCopied/$BytesTotal)*100,1))% kopiert"
} else {
Write-Host -ForegroundColor red "$([math]::round(($BytesCopied/$BytesTotal)*100,1))% kopiert"
}
Write-host -ForegroundColor gray " ... $($Staging.Count -1) Dateien"
Write-host -ForegroundColor gray " ... $([math]::round(($BytesCopied/1024/1024/1024),3)) GB"
write-host ''
# Sicherung in Superlog schreiben
"$Platte;$(get-date -UFormat "%d-%m-%Y");$Sicherung;$FileCount;$BytesTotal;$ZielVerzeichnis;$($Staging.count -1);$BytesCopied" | out-file $RC_Superlog -Append
} else {
# Sicherung in Superlog schreiben
write-host -ForegroundColor gray " ... " -NoNewline
write-host -ForegroundColor green "Sicherung ist aktuell"
write-host ''
"$Platte;$(get-date -UFormat "%d-%m-%Y");$Sicherung;$FileCount;;;;" | out-file $RC_Superlog -Append
}
}
#Ende
write-host -ForegroundColor White "Alle Sicherungen wurden abgeschlossen."
write-host -ForegroundColor DarkGray "Fenster schließt sich in 30 Sekunden."
Start-Sleep -Seconds 30
Mein Fazit
- Optisch und informativ gefällt es mir so sehr gut
- Ich denke, das man hier und da mit Sicherheit noch etwas verbessern kann. Für Vorschläge bin ich immer offen.
Erweiterung
- Da das Skript immer nur eine Mirror-Copy anlegt, möchte ich es gerne noch erweitern. Zu löschende Dateien sollen zuvor noch einmal separat gesichert werden. Dazu habe ich hier im Forum schon etwas gefunden. Allerdings muss ich es noch verstehen und dann in Powershell implementieren.
Gruß Sam
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 294353
Url: https://administrator.de/contentid/294353
Ausgedruckt am: 22.11.2024 um 07:11 Uhr
3 Kommentare
Neuester Kommentar
Ich bin selber nicht der Powershell Papst aber wäre es nicht praktischer die Sicherungslaufwerke in ein einziges multidimensionales Array zu speichern anstatt in 3 Arrays? Dann wäre eine Zeile immer ein Laufwerk und das Rechnen mit $i entfiele.
Habe ich jetzt nicht getestet aber ich habe mir die Infos aus diesen Seiten raus gesucht:
http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/10/create-a- ...
http://stackoverflow.com/questions/9397137/powershell-multidimensional- ...
Auf jeden Fall vielen Dank für Dein Script. Ich werde es sicherlich ausprobieren. Sieht sehr vielversprechend aus.
Tschüß
Bernie
$HDDDestination = @()
$HDDDestination += , "HDDSerials"="123456789", "HDDShort"="WD_Black_1TB", "HDDLong"="Western Digital Black 1TB vollverschlüsselt"
$HDDDestination += , "HDDSerials"="-123456789", "HDDShort"="WD_Purple_3TB", "HDDLong"="Western Digital Purple 3TB vollverschlüsselt"
http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/10/create-a- ...
http://stackoverflow.com/questions/9397137/powershell-multidimensional- ...
Auf jeden Fall vielen Dank für Dein Script. Ich werde es sicherlich ausprobieren. Sieht sehr vielversprechend aus.
Tschüß
Bernie
Hallo Sam,
Hallo andere User,
das Script und der Post zu "Robocopy mit Powershell und Fortschrittsanzeige" liegt hier schon eine Weile. Dennoch habe ich eine Bitte an alle die mir helfen können oder wollen. ich würde gerne das Script anwenden, aber ich bekomme es einfach nicht hin, es so umzuschreiben, dass das komplizierte Einlesen der Laufwerke wegfällt und ich statt dessen einfach zwei Pfade (Quelle und Ziel) angeben kann.
Kann mir dabei Jemand helfen?
Das wäre echt cool. Vielen Dank schon mal...
Ronald
Hallo andere User,
das Script und der Post zu "Robocopy mit Powershell und Fortschrittsanzeige" liegt hier schon eine Weile. Dennoch habe ich eine Bitte an alle die mir helfen können oder wollen. ich würde gerne das Script anwenden, aber ich bekomme es einfach nicht hin, es so umzuschreiben, dass das komplizierte Einlesen der Laufwerke wegfällt und ich statt dessen einfach zwei Pfade (Quelle und Ziel) angeben kann.
Kann mir dabei Jemand helfen?
Das wäre echt cool. Vielen Dank schon mal...
Ronald