samtrex
Goto Top

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:
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" }  
Dabei wird in der Datei copyjob.txt je das zu sichernde Verzeichnis angegeben. Dieses wird dann vom Server auf die Platte gesichert.
PRO
    • Einfach
    • Schnell
NEG
    • 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 ...
c5586e7f1365086bc75e474b16db3dbe

und in den Logs etwas nachvollziehbarer zu machen.
0936b2f337b3ab8a775e882cff251a23
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.
    1. Anhand dessen und der Daten aus dem ersten Flugmodus-Lauf wird die Differenz berechnet und eine Progressbar angesteuert
    2. 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

Content-ID: 294353

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

Ausgedruckt am: 22.11.2024 um 07:11 Uhr

Bernie429
Bernie429 01.02.2016 aktualisiert um 16:00:52 Uhr
Goto Top
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.

$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"  
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
SamTrex
SamTrex 08.02.2016 um 07:54:43 Uhr
Goto Top
Hallo Bernie,
vielen Dank für den Tipp. Mich hat es auch geärgert das ich es nicht anders hin bekommen habe. Ich werde mir Deine Links einmal anschauen und das - wenn ich es hin bekomme - abändern.
Gruß Sam
Sittsoft
Sittsoft 02.12.2019 um 08:03:12 Uhr
Goto Top
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