support-m
Goto Top

Kleines Entpack-Script

Hallo zusammen,
ich bin auf der Suche nach einem einfachen kleinen Script.

Soll: Einen lokalen Ordner überwachen, warten, bis eine ZIP-Datei erstellt wird (mit beliebigem Namen) und diese soll dann ins gleiche Verzeichnis entpackt werden und die ZIP soll im Anschluss automatisch gelöscht werden.

Ich habe folgendes versucht, basierend darauf: https://it-learner.de/mithilfe-der-windows-powershell-einen-ordner-bzw-e ...

# Variablen definieren
$ordner = 'D:\Pfad\zum\Ordner'  
$timeout = 10
$DateiFilter = '*.zip'  
Write-Host $ordner

$zipwatcher = New-Object System.IO.FileSystemWatcher $ordner, $DateiFilter

# Manueller Abbruch der Schleife
Write-Host „Mit CTRL+C kann das Monitoring abgebrochen werden.“
Remove-Item -Path $ordner'\*.zip' #für den Fall, dass schon eine ZIP existiert  

while ($true) {
$result = $zipwatcher.WaitForChanged(‚all‘, $timeout)
if ($result.TimedOut -eq $false)
    {
    Write-Host ('ZIP {0}: {1}‘ -f $result.ChangeType, $result.name) #veränderte ZIP-Datei anzeigen  
    Expand-Archive -Path $ordner'\*.zip' -DestinationPath $ordner -Force #ZIP entpacken  
    Write-Host $result.name ' entpackt'  
    Start-Sleep -Seconds 5
    Remove-Item -Path $ordner'\*.zip' # ZIP löschen  
    Write-Host $result.name ' gelöscht'  
    } #end if
} #end while

Write-Host „Monitoring abgebrochen.“
Das funktioniert manchmal. Dann aber auch wiederum nicht. Ein anderes Mal konnte ich 10 mal die ZIP Datei kopieren, entpacken und wieder löschen lassen, beim 11. Mal ging es dann plötzlich nicht mehr.

Was mache ich falsch? Gibt es einen einfacheren Weg? Die Ausgaben sind nice-to-have, wichtiger wäre die Grundfunktion des Scripts.

Kann mir jemand helfen? face-smileI

Danke!

MfG

Content-Key: 6543517771

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

Printed on: July 20, 2024 at 16:07 o'clock

Member: Kraemer
Kraemer Mar 28, 2023 updated at 14:04:08 (UTC)
Goto Top
Moin,

da steht alles beschrieben: https://powershell.one/tricks/filesystem/filesystemwatcher

Es wird daran liegen, das der Watcher die Änderung schlicht "übersieht". Das Start-Sleep wird das Problem sogar noch verschlimmern.
Member: Crusher79
Crusher79 Mar 28, 2023 at 17:15:52 (UTC)
Goto Top
https://powershell.one/tricks/filesystem/filesystemwatcher

Der hat sich viele Gedanken zum Watcher gemacht.

Alternativ kannst du auch so ZIP prüfen und entpacken. Entweder Lock prüfen - ob die noch geschrieben wird - oder Sagen es soll nur Dateien nehmen die mindestens 5 min. alt sind. Dann sollten die - je nach Größe - vollständig geschrieben sein und keinen Lock aufweisen. Ab da dann wieder normal weiter.

Schleife oder Task der alle 5 min. prüft. Je nachdem, wie Zeitkritisch es sein soll.

Für Backup hab ich immer selber eine Lock File erstellt. Um zu verhindern das Task erneut ausgeführt wird, wenn der alten noch laufen/ hängen sollte. Wurde eine gewisse Zeit überschritten, wurden die Altlasten entsorgt und Lock gelöscht. Dann war wieder alles soweit für einen erneuten Aufruf.

"Hotfolder" Überwachung ist schön, hat aber so seine Tücken. Man kann es auch anders lösen.
Member: support-m
support-m Mar 30, 2023 at 09:11:46 (UTC)
Goto Top
Hallo zusammen,
danke für eure Antworten. Deswegen habe ich mich nun vom Watcher getrennt und habe folgenden eigenen Code (ja ich bin trotz des Billig-Scripts stolz) geschrieben:

while ($true) {

try {
Expand-Archive -Path 'Pfad\zur\*.zip' -DestinationPath 'Pfad\zur' -force  
Write-Host 'ZIP entpackt'  
Remove-Item 'Pfad\zur\*.zip'  
Write-Host 'ZIP geloescht'  
}
catch {
Write-Host 'Keine ZIP vorhanden'  
}
finally {
Start-Sleep -Seconds 300
}
} #end while

Das scheint zu funktionieren. Eine Frage, kann es hier Probleme geben, wenn ständig das PowerShell Script in einer while-Schleife im Hintergrund läuft? Läuft der RAM voll oder ähnliches? Wie kann ich das Script eventuell ressourcenschonender anpassen? Es reicht, wenn das alle paar Minuten ausgeführt wird.

Ich habe festgestellt, wenn keine ZIP-Datei vorhanden ist, kommt in der ISE-Konsole so ein Balken:
zip-entpacken

Allerdings füllt sich der nicht und sobald eine ZIP-Datei im Ordner ist, verschwindet der auch wieder, kommt danach dann aber wieder, nachdem die ZIP gelöscht wurde. Ist das problematisch? Wenn ich das Script direkt als .ps1-Script ausführe, sehe ich eine ähnliche Ansicht.

Danke!

MfG
Member: Kraemer
Kraemer Mar 30, 2023 at 09:14:48 (UTC)
Goto Top
Moin,

an dem Skript ist so ziemlich alles Murks. Das liegt vor allem aber daran, dass du deine eigenen Vorgaben nicht konsequent verfolgst.
Member: support-m
support-m Mar 30, 2023 at 09:28:58 (UTC)
Goto Top
Zitat von @Kraemer:

Moin,

an dem Skript ist so ziemlich alles Murks. Das liegt vor allem aber daran, dass du deine eigenen Vorgaben nicht konsequent verfolgst.

Könntest du das genauer ausführen? Es macht genau das, was es soll. Nur dass es nicht erst aktiv wird, wenn eine ZIP Datei erstellt wird, sondern einfach versucht, eine ZIP-Datei zu entpacken und wenn das nicht klappt schreibt es "Keine ZIP vorhanden" und wartet 5 Minuten.
Member: Kraemer
Kraemer Mar 30, 2023 at 10:18:50 (UTC)
Goto Top
Zitat von @support-m:

Könntest du das genauer ausführen?

ja, kann ich.

Wenn dein Skript wo auch immer einen entsprechenden Fehler wirft, wird es sich beenden und du bekommst das nicht einmal mit.

while ($true) ist igitt - es gibt eigentlich kein Fall, wo dass Sinn ergibt.

Du brauchst also ein Skript, welches alle 5 Minuten ausgeführt wird. Dann bau das auch. Nutze die Aufgabenplanung.

Dann nimmst du deine Zips, und prüfst, ob die Datei noch geöffnet ist.
Habe mal schnell was aus dem Internet kopiert und nicht getestet:

Function Confirm-FileInUse {
    Param (
        [parameter(Mandatory = $true)]
        [string]$filePath
    )
    try {
        $x = [System.IO.File]::Open($filePath, 'Open', 'Read') # Open file  
        $x.Close() # Opened so now I'm closing 
        $x.Dispose() # Disposing object
        return $false # File not in use
    }
    catch [System.Management.Automation.MethodException] {
        return $true # Sorry, file in use
    }
}
Quelle: https://stackoverflow.com/questions/24992681/powershell-check-if-a-file- ...

Wenn die Datei nicht gesperrt ist, entpackst du diese und löscht die danach. Ein Sleep ist nicht notwendig.

Dann garnierst du das Ganze noch mit einem ordenlichen Log und fertig ist der Zauber
Member: Crusher79
Crusher79 Mar 30, 2023 at 20:48:19 (UTC)
Goto Top
"Billig" ist egal !

Ich achte bei sowas immer darauf dass es robust läuft. Ein "toll, super" wirst du eh nicht bekommen. Nur Ärger wenn es nicht sauber läuft. face-wink Von daher alles gut. Hübsch machen kann man den Code ggf. noch - einrücken. Das sollte es dann gewesen sein.

Hier nochmal 2 Funktionen die ich verwende um den Scheduled Task vor einen "runaway" zu schützen. Prüfung auf Lock File verhindert dass Task - der z.B. alle 5 min. läuft - gestartet wird, wenn der davor noch nicht fertig ist. Gerade auch bei Datei Operationen wäre das nicht so schön.

Nur irgendwann passiert sowas vlt. doch mal. Wie dann auflösen? Ganzen Server neustarten oder Mails versenden? Je nach Vorgang kann man ggf. mit taskkill dann die alten Dinger weghauen. Was ist aber mit der Lockfile? Hier unterstelle ich das nach über 10 min. alles gut ist. Die wird dann gelöscht und neu erstellt. Somit würde sich je nach schwere des Problems das Ganze von selber auflösen.

Kommt aber stark auf die Tasks an. Manche haben ja naturbedingt einen Timeout. Wenn wir z.B. einen Webservice konsumieren.

Noch sauberer wäre es wohl, wenn man die Prozess ID festhält und nur diesen später ggf. hart killt.

[System.Diagnostics.Process]::GetCurrentProcess().Id

Wenn man das noch in die Lock Datei schreibt hätte man eine eindeutig Referenz. Wenn die PS natürlich anddre Prozesse anstößt und die nicht im gleichen Context laufen muss man die noch behandeln.


Nur so eine Idee! Aber mit Lock-Files und Process ID kannst du es noch etwas absichern. Dann eben alle 5 min. starten. Ist ja kein Java face-wink Bei C# haben wir ja auch einen Garbarge Collector!

Ansonsten versuchen immer sauber zu beenden. Zu jedem MS SQL Connect gehört auch ein Disconnect. Wenn du später noch an solche Sachen denkst sollte das ganze den RAM nicht sprenge. Oder gar 1.000 "Kopien" auf einmal offen sein.

Nur so Gedanken ....


Function CreateLckFile() { 
            $null = New-Item -ItemType "file" -Path $lckFile;   
            # Fix NTFS Tunnellig
            $(Get-Item -Force $lckFile).CreationTime = $(Get-Item -Force $lckFile).LastWriteTime; 
            }

Function LckTimeDiff() {
            $lckFileCreationTime = $((Get-Item $lckFile).CreationTime)
            $Diffobj = $(Get-Date) - $lckFileCreationTime
            [int]$Script:DiffInMin = [int]$Diffobj.TotalMinutes
            }

Function LckAndStart() {  
    if (! (Test-path -path $lckFile)) {
        CreateLckFile
        WriteLog "Lck Erstellt"  
        RestartBMV    
        WriteLog "Restart abgeschlossen"         
        Remove-Item $lckFile -Force
    } else { 
        LckTimeDiff                            
        IF ($DiffInMin -gt 10 ) {                            
            WriteLog "Alte Lck vorhanden"  
            Remove-Item $lckFile -Force   
            CreateLckFile
            WriteLog "Lck neu erstellt"  
            RestartBMV     
            WriteLog "Restart abgeschlossen"     
            Remove-Item $lckFile -Force                
            } 

    }
}
Member: Crusher79
Crusher79 Mar 30, 2023 updated at 21:06:56 (UTC)
Goto Top
Noch eine Ergänzung! Schreib Log Files! dann bekommst du es einfacher mit, wenn was schief geht. Nutze eine Function und häng es dran.

Function Script:WriteLog() {
            Param ([string]$LogString)
            $LogFile = "C:\logs\$(gc env:computername).log"  
            $DateTime = "[{0:dd/MM/yyyy} {0:HH:mm:ss}]" -f (Get-Date)  
            $LogMessage = "$Datetime $LogString"  
            Add-content $LogFile -value $LogMessage
            }

Move-Item -Path $zipFileName -Destination d:\ -ErrorAction SilentlyContinue
 if(-not $?) { $Script:MOVEErrorMsg = "Copy Failed: " + $error[0].exception.message }  

try .... ist ja bekannt. if(-not $?) mit $error[0].exception.message liefert auch brauchbare Fehlermeldungen.

Ggf. das in Kombination einbauen. Fehlerbehandlung könnte sein die Dateien zu verschieben und Lock zu releasen. Oder eine E-Mail abzusetzen.

So oder so würde ich mehr Wert auf das Error Handling an deiner Stelle legen. Vermutlich bringt dir das mehr, als wenn das System durch Dauerschleifen an seine Grenzen kommen würde. Wenn du - wie auch von @Kraemer erwähnt - ein Script alle 5 Minuten ausführst sollte nicht viel passieren.


Error Handling, doppelte Ausführung verhindern - das würde ich dann als "robustes Script" verstehen.

Arbeite mit Functions. Die tun nicht weh. Einmal diese paar 5 Zeiler da abgesetzt und alles ist rund. Der Call ist ja nur ein Wort. Mitunter muss nicht mal ein Parameter mitgegeben werden.

Zack fertig.