herbstengel
Goto Top

Powershell: Trick gesucht zur Beschleunigung eines Dateivergleichs

Hallo Community,

ich habe folgendes Skript, um gleichnamige Dateien in zwei unterschiedlichen Verzeichnissen zu vergleichen. Herzstück ist cmdlet Compare-Object. Funktioniert an sich (Gutfall und Schlechtfall), nur dauert es bei entsprechend großen Dateien eine (ganze) Weile, bis die Abarbeitung zu Ende ist. Leider gibt es bei Compare-Object kein -AsJob, um die Abarbeitung in den Hintergrund zu schieben und um somit gleich ohne nennenswerte Verzögerung mit einer möglichen Nachfolge Powershell-Skriptsequenz parallel zur Hintergrundverarbeitung weiterzumachen. Mit Receive-Job ließe sich ja das Ergebnis auswerten.

Gibt es trotzdem einen Trick, um das zu bewerkstelligen oder gehört mein Code komplett anders aufgebaut?

Viele Grüsse, Roger

[bool]$FinishLoop = $false
$dir1 = "C:\test" # Verzeichnis 1  
$dir2 = "D:\test" # Verzeichnis 2  

clear-host

Compare-Object (Get-ChildItem $dir1 -File).Name (Get-ChildItem $dir2 -File).Name -IncludeEqual -ExcludeDifferent -PassThru | ForEach-Object{
# Inhalt der Dateien vergleichen
$diff = Compare-Object (Get-Content "$dir1\$_") (Get-Content "$dir2\$_")  
# Gibt es einen Unterschied , ForEach-Object-Schleife abbrechen
    if ($diff -and !$FinishLoop)
    {
        $FinishLoop = $true
    }
}


if($FinishLoop -eq $true)
{
    write-host "Unterschiede in den Dateien" -F Red  
}
else
{
    write-host "keine Unterschiede in den Dateien" -F Green  
}

Content-ID: 562682

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

Ausgedruckt am: 21.11.2024 um 20:11 Uhr

SeaStorm
Lösung SeaStorm 02.04.2020 aktualisiert um 22:48:29 Uhr
Goto Top
Hi

also grundsätzlich würde ich hier vielleicht eher mit Checksummen arbeiten, also mit Get-FileHash . Kommt drauf an ob du die Unterschiede dann aufgelistet haben willst oder nicht. Wenn es dir nur darum geht rauszufinden ob die Dateien gleich sind oder nicht, dann macht eine Checksum wohl mehr sinn.

Ansonsten kannst du natürlich aber alles per Start-Job in Jobs schmeissen. einfach in einer Schleife alle Files im Ordner durchgehen und z.B immer 5 Dateien auf ein mal vergleichen
Pjordorf
Pjordorf 02.04.2020 um 22:44:39 Uhr
Goto Top
Hallo,

Zitat von @Herbstengel:
Gibt es trotzdem einen Trick, um das zu bewerkstelligen oder gehört mein Code komplett anders aufgebaut?
Ja, Leistungsfähigere Hardware verwenden die das in deiner gewünschten Sekunde kann. Oder deine Daten soweit runterbrechen das die nicht so lange berechnet werden müssen.

Gruß,
Peter
TK1987
TK1987 02.04.2020 aktualisiert um 23:21:39 Uhr
Goto Top
Moin,

Zitat von @SeaStorm:
also grundsätzlich würde ich hier vielleicht eher mit Checksummen arbeiten
Entweder das - oder halt die Dateigröße vergleichen. Die Chance, das 2 Dateien mit selbem Namen aufs Byte genau gleich groß sind und dennoch einen unterschiedlichen Inhalt aufweisen ist eben auch nur sehr sehr gering.

Gruß Thomas
StefanKittel
StefanKittel 03.04.2020 um 10:26:01 Uhr
Goto Top
Moin,

das sind zwei aufbauende Themen.
Ich will extra nicht Probleme schreiben.

1. Warten.
Wenn Du nicht warten willst, verwende Multithreading
Wenn Deine Umgebung kein Multithreading kann verwende eine Andere.

Bringt Dir vermutlich aber nichts weil Du nicht 1000 100GB-Dateien gleichzeitig lesen kannst weil die Hardware das nicht mitmacht.
Also warten bis er fertig ist oder und 2. schauen.

2. Vergleichen
Zuerst Dateigröße, Änderungsdatum und Attribute vergleichen.
Wenn hier was abweicht brauchst Du den Rest nicht testen.

Dann Prüfsumme des ersten und letzten MBs erstellen und vergleichen.
Wenn hier was abweicht brauchst Du den Rest nicht testen.

Jetzt die Knuffifrage ob Du in diesem Fall auch die ganze Datei vergleichst oder nicht.
Musst Du entscheiden.

Stefan
Herbstengel
Herbstengel 03.04.2020 aktualisiert um 21:30:25 Uhr
Goto Top
ACHTUNG: Status gelöst stimmt noch nicht !!!

Ich habe das mal mit Start-Job aufgebaut, allerdings stimmt nach Abarbeitung des Hintergrundprozesses im "Schlechtfall" der Inhalt von $FinishLoop nicht: d.h. Wert von $FinishLoop ändert sich nicht auf $true. Woran kann das liegen? Ausserdem läuft mit Start-Job der Vergleich im Hintergrund viel schneller ab als ohne Start-Job...

[bool]$FinishLoop = $false
$dir1 = "C:\test" # Verzeichnis 1  
$dir2 = "D:\test" # Verzeichnis 2  

clear-host

Start-Job -ScriptBlock {Compare-Object (Get-ChildItem $dir1 -File).Name (Get-ChildItem $dir2 -File).Name -IncludeEqual -ExcludeDifferent -PassThru | ForEach-Object{
# Inhalt der Dateien vergleichen
$diff = Compare-Object (Get-Content "$dir1\$_") (Get-Content "$dir2\$_")  
# Gibt es einen Unterschied , ForEach-Object-Schleife abbrechen
    if ($diff -and !$FinishLoop)
    {
        $FinishLoop = $true
    }

}} -Name MyJob

while ((Get-Job -Name MyJob).State -ne "Completed")  
{
    Write-Host "Vergleich laeuft, mache dann was anderes"  
    #hier weitere Aktion starten
}
Write-Host ""  
Write-Host "fertig"  

#Ergebnis auswerten
if($FinishLoop -eq $true)
{
    write-host "Unterschiede in den Dateien" -F Red  
}
else
{
    write-host "keine Unterschiede in den Dateien" -F Green  
}

#Job abloeschen
Get-Job -Name MyJob | Remove-Job  

Grüsse, Roger
Pjordorf
Lösung Pjordorf 03.04.2020 um 21:46:40 Uhr
Goto Top
Hallo,

Zitat von @Herbstengel:
> [bool]$FinishLoop = $false
> 
Woran kann das liegen?
Der wird sich nicht ändern da du den nicht änderst, nur abfragst.

Gruß,
Peter
Herbstengel
Herbstengel 03.04.2020 aktualisiert um 22:05:35 Uhr
Goto Top
Hallo Peter,

doch, hier:

if ($diff -and !$FinishLoop)
    {
        $FinishLoop = $true
    }
Pjordorf
Lösung Pjordorf 03.04.2020 um 23:57:14 Uhr
Goto Top
Hallo,

Zitat von @Herbstengel:
doch, hier:
> if ($diff -and !$FinishLoop)
>     {
>         $FinishLoop = $true
>     }
> 
Dann wird diser Code nie erreicht...Deine IF ist **korrekt?

Gruß,
Peter
Herbstengel
Herbstengel 05.04.2020 aktualisiert um 22:07:48 Uhr
Goto Top
Hallo Peter,

mein Fehler war, dass ich in den scriptblock von Start-Job Parameter von außen bringen muss, damit die interne Verarbeitung
korrekt funktioniert. Mit diesem sepaatem Thread

Powershell: Unterstützung bei Start-Job benötigt im Hinblick auf Ein- und Ausgabeparameter


habe ich mir die Anwendung von Start-Job zunächst klar gemacht und dann auf diese Anwendung angewandt.

Ergebnis: s.u.

Thread nun hiermit wirklich abgeschlossen face-smile


[string]$dir1 = "D:\Quelle" # Verzeichnis 1  
[string]$dir2 = "D:\Ziel" # Verzeichnis 2  
[bool]$finishLoop = $false

#initialisierung Ausgabevariable/Objekt
[PSCustomObject]$returnObj=  @()
[bool]$resultFirstHitInLoop = $false

[string]$backgroundJobName = "MySpecialBackgroungJob"  

clear-host


start-job `
   -argumentlist $dir1, $dir2, $finishLoop `
   -Name $backgroundJobName `
   -scriptblock `
   {
       param ($p_dir1,$p_dir2,$p_finishLoop) #p_ steht fuer Parameter

       [PSCustomObject]$diff = $null

       #Rueckgabewerte der Routine
       $retObj = [PSCustomObject] `
        @{
            finishLoop    = $false
            # ggf. weitere Attribute
         }

       #Routine: verarbeitet Eingaben, liefert Ausgaben
       Compare-Object (Get-ChildItem $p_dir1 -File).Name (Get-ChildItem $p_dir2 -File).Name -IncludeEqual -ExcludeDifferent -PassThru | ForEach-Object `
       {
           # Inhalt der Dateien vergleichen
           $diff = Compare-Object (Get-Content "$p_dir1\$_") (Get-Content "$p_dir2\$_")  
           # Gibt es einen Unterschied , ForEach-Object-Schleife abbrechen
           if ($diff -and !$p_finishLoop)
           {
               $p_finishLoop = $true

               $retObj.finishLoop = $p_FinishLoop

               return ($retObj)
           }

       }
   }

while ((Get-Job -Name $backgroundJobName).State -ne "Completed")  
{
    Write-Host "Verarbeitung von $backgroundJobName laeuft im Hintergrund, mache dann ggf. parallel zur diesem Hinweis was anderes"  
    #hier weitere Aktion starten
}

Write-Host ""  
Write-Host "Verarbeitung von $backgroundJobName im Hintergrund wurde beendet"  
Write-Host ""  
Write-Host "Darstellung der Verarbeitung:"  

#Verarbeitungsergebnis wird abgeholt
$returnObj = (Get-Job -Name $backgroundJobName) | receive-job
#Job wird dann gleich geloescht
(Get-Job -Name $backgroundJobName) | remove-job

#Ausgabeteil: in Variblen und aus Konsole
Write-Host ""  
Write-Host "----E R G E B N I S---"  

Write-Host "Fertig!"  
Write-Host ""  

if($null -ne $returnObj)
{
    $resultFirstHitInLoop = $returnObj | Select-Object -ExpandProperty finishLoop
}

if($resultFirstHitInLoop -eq $true)
{
    write-host "Unterschiede in den Dateien" -F Red  
}
else
{
    write-host "keine Unterschiede in den Dateien" -F Green  
}

Write-Host "----------------------"  
Write-Host "-------E N D E--------"