sugram
Goto Top

Script funktionier im ISE, aber nicht wenn ich es per cmd aufrufe

Hallo

Ich habe hier ein Script das im Powershell ISE einwandfrei funktioniert.
Es kopiert sobald neue Dateien in einem Verzeichnis geschrieben werden, diese noch auf einem anderen Server.
Wenn ich es in einer Batch starte, dann kopiert er mir die Dateien nicht mehr.
Ich rufe es normalerweise so auf:

start /min powershell -WindowStyle Hidden -Command "& {$host.ui.RawUI.WindowTitle = 'Copy-Server'; C:\Batch\script\Copy.ps1 }"  

Jetzt wo es nicht funktionert, natürlich ohne -WindowStyle Hidden und /min

Das Script selbst ist dieses hier:

$verzeichnisse = @{
    "\\server19\Vision\EM\ST300\xxxx\OK" = "\\server11\INCOME\EM\ST300"  
    "\\server19\Vision\EM\ST500\xxxx\OK" = "\\server11\INCOME\EM\ST500"  
    "\\server19\Vision\EM\ST600\xxxx\OK" = "\\server11\INCOME\EM\ST600"  
    "\\server19\Vision\EM\ST700\xxxx\OK" = "\\server11\INCOME\EM\ST700"  
}

# Überwachungsfilter
$filter = "*.*"  

$watchers = @()

# Erstelle einen FileSystemWatcher für jeden Quellpfad
foreach ($quelle in $verzeichnisse.Keys) {
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $quelle
    $watcher.Filter = $filter
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true

    $eventIdentifier = "FileCreated_$($quelle.Replace('\', '_').Replace(':', ''))"  
    
    # Event, das ausgelöst wird, wenn eine neue Datei erstellt wird
    $event = Register-ObjectEvent -InputObject $watcher -EventName Created -SourceIdentifier $eventIdentifier -Action {
        $neueDatei = $Event.SourceEventArgs.FullPath
        Write-Host "Neue Datei erstellt: $neueDatei"  

        # Finde das passende Zielverzeichnis
        $zielVerzeichnis = $verzeichnisse.GetEnumerator() | Where-Object { $neueDatei.StartsWith($_.Key) } | ForEach-Object { $_.Value }
        if ($zielVerzeichnis -eq $null) {
            #Write-Host "Zielverzeichnis nicht gefunden für: $neueDatei" 
            return
        }

        # Bestimme den relativen Pfad der Datei
        $relativerPfad = (Get-Item $neueDatei).FullName.Substring($quelle.Length + 1)

        # Erstelle die Unterverzeichnisse im Zielverzeichnis, falls sie nicht existieren
        $zielPfad = Join-Path $zielVerzeichnis $relativerPfad
        $zielVerzeichnis = Split-Path $zielPfad
        if (-not (Test-Path $zielVerzeichnis)) {
            New-Item -Path $zielVerzeichnis -ItemType Directory -Force
           Write-Host "Unterverzeichnis erstellt: $zielVerzeichnis"  
        }

         #Kopiere die neue Datei zum Zielverzeichnis
        Copy-Item -Path $neueDatei -Destination $zielPfad -Force

        Write-Host "Datei wurde nach $zielPfad kopiert."  
    }

    
    $watchers += $watcher

    Write-Host "Überwachung für $quelle gestartet."  
}


try {
    while ($true) {
        # Unendliche Schleife zum Aufrechterhalten des Skripts
        Start-Sleep -Seconds 1
    }
} finally {
    
    foreach ($watcher in $watchers) {
        $eventIdentifier = "FileCreated_$($watcher.Path.Replace('\', '_').Replace(':', ''))"  
        Unregister-Event -SourceIdentifier $eventIdentifier
        $watcher.Dispose()
    }
}

Wenn ich diesen Code im ISE Starte und neue Dateien kommen, kopiert er mir diese auch auf den Zielpfad.
Allerdings wenn ich das Script dann per Batch starte, zeigt er mir nur noch an, daß die Datei erstellt wird, aber nicht mehr das sie kopiert wird.
Weiß hier zufällig einer weshalb das so ist, bzw. wie man das gelöst bekommt?

Content-Key: 72439107725

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

Printed on: April 27, 2024 at 08:04 o'clock

Mitglied: 11078840001
Solution 11078840001 Feb 05, 2024 updated at 21:53:26 (UTC)
Goto Top
Stichwort Variable Scope
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell ...
Dein Code im Action-Eventblock wird in einem anderen Scope ausgeführt und kennt deswegen deine Variable $verzeichnisse nicht, ergo kein Zielpfad ergo keine Kopie da du mit return das Event in Zeile 32 vorzeitig verlässt.
Also übergebe die Variable entweder per -MessageData Parameter an den Action-Scriptblock oder definiere die Variable als "Globale" Variable. Globale Variablen sind aber in der Regel schlechter Stil daher hier mal ein
Beispiel mit übergabe via MessageData
Das erledigt dann auch gleich das super ineffektive Ermitteln des Zielpfades in deinem Code gleich mit.

☠️
Member: Celiko
Celiko Feb 05, 2024 at 23:59:03 (UTC)
Goto Top
Moin,

try {
    while ($true) {
        # Unendliche Schleife zum Aufrechterhalten des Skripts
        Start-Sleep -Seconds 1
    }
} finally {
    
    foreach ($watcher in $watchers) {
        $eventIdentifier = "FileCreated_$($watcher.Path.Replace('\', '_').Replace(':', ''))"    
        Unregister-Event -SourceIdentifier $eventIdentifier
        $watcher.Dispose()
    }
}

Bin mir unsicher, ob ich falsch liege... aber das Script geht nie aus der While-Schleife raus.
Also wird es letztlich auch nie die Watcher disposen, oder?
Korrigiert mich gerne face-smile

VG
Mitglied: 11078840001
11078840001 Feb 06, 2024 updated at 06:59:15 (UTC)
Goto Top
Zitat von @Celiko:
Bin mir unsicher, ob ich falsch liege... aber das Script geht nie aus der While-Schleife raus.
Also wird es letztlich auch nie die Watcher disposen, oder?
Korrigiert mich gerne face-smile
Doch das ist schon richtig so, da ohne eine Schleife sich die Shell direkt wieder schließen würde und dadurch die Events nicht feuern könnten.
Das sleep 1 kann man aber auch durch die WaitForChanged Methode ersetzen das ist noch etwas effizienter
https://learn.microsoft.com/de-de/dotnet/api/system.io.filesystemwatcher ...

Durch das TryCatch werden die Handler bei einem Abbruch der Schleife mit CTRL+C anschließend im finally Block der am Ende immer ausgeführt wird, disposed. Das ist aber nur notwendig wenn die Konsole danach noch offen bleibt denn ansonsten würden die Objekte samt Handler mit Events beim Schließen der PS-Konsole eh automatisch zerstört.
Member: sugram
sugram Feb 06, 2024 at 12:45:34 (UTC)
Goto Top
Zitat von @11078840001:

Stichwort Variable Scope
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell ...
Dein Code im .........
☠️

Hallo

erst einmal vielen Dank für die Antwort.
Ich gebe zu da jetzt erst einmal "Bahnhof" zu verstehen.
Denn ich war froh als das Script endlich funktionierte. Hat mich einige Recherchen und Try & error gekostet.
Ich hoffe das ich das dann hinbekomme, damit es auch ohne ISE funktioniert.
Das mit dem ermitteln des Zielpfades habe ich dann deswegen gemacht (ok per google gesucht) weil er mir die Datei immer in das erste Zielverzeichnis kopiert hat.
Mitglied: 11078840001
11078840001 Feb 06, 2024 updated at 13:28:42 (UTC)
Goto Top
Ich gebe zu da jetzt erst einmal "Bahnhof" zu verstehen.
Nun der Action Skriptblock läuft in einem Event-Skriptblock der quasi separat ausgeführt wird und somit kennt der die Variablen die außerhalb von diesem Skriptblock deklariert wurden nicht. In deinem Fall ist das die Variable $verzeichnisse, die ist dann innerhalb des Blocks leer. Wenn du diese als $global:verzeichnisse deklarieren würdest wäre sie auch im Eventblock verfügbar. Da die Verwendung von $global aber schlechter Stil ist habe ich dir oben das Beispiel mit der Übergabe der Variable über den MessageData-Parameter gezeigt.
Das löst dann auch gleich das ineffiziente ermitteln des Zeilpfades gleich mit face-smile

Die ISE gaukelt einem hier oft was vor weil die die Variablen am Ende nicht wieder löscht. Deswegen ist die ISE beim Entwickeln immer mit Vorsicht zu genießen.

Also einfach noch mal in Ruhe die Seiten lesen und meinen Beispielcode oben verstehen, ein Meister fällt ja nicht vom Himmel.
Lesen und Verstehen ist am Ende des Tages einfach nachhaltiger als mit Copy n Paste sich hier alles vorbeten zu lassen.
Member: sugram
sugram Feb 06, 2024 at 13:54:52 (UTC)
Goto Top
Das muß ich mir jetzt wirklich ganz genau durchlesen.
Denn bis ich mein Script zum laufen gebracht habe, das hat auch ne ganze weile gedauert face-wink