Dynamisches Hinzufügen von Aufgaben zu einer PowerShell-Jobwarteschlange

2

Ich habe ein PowerShell-Skript erstellt , das alle von mir heruntergeladenen E-Books in ein vorbestimmtes Verzeichnis kopiert, das von meinem E-Book-Manager regelmäßig gescannt und meiner Bibliothek hinzugefügt wird. Das Skript wird sofort nach jedem Download ausgeführt.

Das Problem ist, dass der E-Book-Manager beim gleichzeitigen oder zeitgleichen Herunterladen mehrerer Bücher abstürzt und nicht mehr reagiert.

Aus diesem Grund möchte ich das Kopieren mithilfe von PowerShell-Jobs in die Warteschlange stellen, kann jedoch keine einzelne Warteschlange (einzelne Parallelität) erstellen, die für jeden nachfolgenden Job auf den Abschluss aller älteren Jobs wartet.

Das heißt, ich möchte, dass das Skript einen Auftrag erstellt (nennen wir ihn "Buchauftrag"), der regelmäßig die Warteschlange für die Ausführung von Buchaufträgen überprüft, um festzustellen, ob alle älteren Buchaufträge abgeschlossen sind, bevor sie ausgeführt werden. Wenn es abgeschlossen ist, sollte ein Buchjob erklären, dass er auf eine Weise beendet wurde, die von jüngeren Buchjobs erkannt werden kann.

Weiß jemand, wie ich das machen kann? Ich habe hier eine ähnliche Frage gesehen, die ich gerade betrachte: Powershell-Hintergrundaufgaben , in meinem Fall führe ich das Skript jedoch mehrfach aus (nach jedem neuen Download).

Mavaddat Javid
quelle
1
Ich möchte dies nicht tun (das Skript muss nach einem bestimmten Zeitraum in den Energiesparmodus versetzt und erneut überprüft werden), da das PowerShell-Skript nach Abschluss jedes Downloads ausgeführt wird (mit einem Eingabeparameter für das spezifische Unterverzeichnis, das die neu heruntergeladenen Dateien enthält). Dies bedeutet, dass sich die Warteschlangenlogik nicht darauf verlassen kann, dass das Skript selbst im Hintergrund bleibt. Ich möchte, dass das Skript einen Job erstellt, der im Hintergrund bestehen bleibt, und prüft, ob es sich um den ältesten Job dieser Art handelt, bevor die Kopierlogik ausgeführt wird.
Mavaddat Javid
Ist die unten stehende Antwort eine vernünftige Lösung für Ihre Bedürfnisse? Ich bin gespannt, ob Sie möchten, dass die Warteschlange für jede nacheinander gefundene Datei wartet, damit jede Ausführung des Skripts eine einzelne Datei ausführt oder nur für die Dateien, die jede Ausführung bei der Ausführung findet, um nur diese Dateien zum Kopieren zu verarbeiten oder was auch immer und nicht Wurde dem überwachten Download-Ordner etwas hinzugefügt, nachdem die Ausführung des Jobs gestartet wurde? Möglicherweise kann es hilfreich sein, einen zusätzlichen Ordner in die Schleife einzufügen und die darin enthaltenen Dateien zu löschen, bevor andere kopiert werden.
Pimp Juice IT

Antworten:

3

Mein Gedanke ist, eine Warteschlange durch Erstellen einer Sperrdatei pro neuer Instanz Ihres Skripts einzurichten. Bei der Ausführung des Skripts wird ein Verzeichnis überprüft, das der Verfolgung der Warteschlange auf vorhandene Instanzen des Skripts dient. Wenn es keine gibt, fügt sich das Skript in die Warteschlange ein, führt eine Aktion aus (führt Ihren Code aus) und bereinigt dann seine Sperre. Wenn Sperren vorhanden sind, wird am Ende der Warteschlange eine neue hinzugefügt, und die Instanz prüft endlos, bis sie sich am Anfang der Warteschlange befindet.

Auf diese Weise können Sie dasselbe Skript mehrmals ausführen, wobei sich alle einzeln durch Überprüfen der extern verfügbaren Warteschlange verhalten.

Die Sperrdateien sind wie folgt aufgebaut: Index, Trennzeichen ("_"), Prozess-ID.

Clear-Host

function New-Lock ([int] $index) {
    $newLock = "$index" + "_" + $pid + ".lck"
    New-Item $queue$newLock | Out-Null
}

$queue = "C:\locks\"

# find the end of the stack
$locks = gci $queue *.lck | sort | select -expandproperty name

# if locks exist, find the end of the stack by selecting the index of the last lock
if($locks) {
    # gets the last lock file, selects the index by splitting on the delimiter
    [int]$last = [convert]::ToInt32(($locks | select -last 1).Split("_")[0],10)

    # add the lock to the end of the stack
    New-Lock ($last + 1)
}
# if no locks exist, create one at the top of the stack
else {
    New-Lock 0
}

# check if we're at the top of the stack
do {
    $locks = gci $queue *.lck | sort | select -expandproperty name

    # this is the PID on the top of the stack
    [int]$top = [convert]::ToInt32(($locks | select -first 1).Split("_")[1].Split(".")[0],10)
    write-verbose "not at the top..."
    sleep 1
} until ($pid -eq $top)

# if we're here, we've been to the top. it's our turn to do something
Write-Verbose "we've reached the top!"
# <do something. put your code here>
# might be good to add some Start-Sleep here
# </do something put your code here>

# now that we're done, let's delete our lock
gci $queue | select -first 1 | Remove-Item

Unten sehen Sie ein fiktives Timeline-Beispiel, in dem Sie drei Dateien heruntergeladen haben (ich habe zufällige PIDs ausgewählt).

  • Datei 1 wird heruntergeladen und startet das Skript. Es sind keine Sperren vorhanden. Erstellen Sie die Sperre "0_19831". Wir sind an der Spitze des Stapels, sodass Ihr Code ausgeführt wird. Dies ist ein großes E-Book, daher dauert die Ausführung Ihres Dateiübertragungscodes eine volle Minute.
  • Datei 2 wird heruntergeladen und startet das Skript. Sperre (n) vorhanden Erstellen Sie die Sperre "1_332". Wir sind nicht an der Spitze des Stapels, also werden wir warten do/untilund weiter nachsehen, bis wir an erster Stelle stehen.
  • Datei 1 wurde kopiert. Sperre "0_19831" löschen.
  • Datei 3 wird heruntergeladen und startet das Skript. Sperre (n) vorhanden Erstellen Sie die Sperre "2_7582". Wir sind nicht an der Spitze des Stapels, warten Sie, bis wir es sind.
  • Datei 2 wurde kopiert. Sperre "1_332" löschen.
  • Datei 3 wurde kopiert. Sperre "2_7582" löschen.

Diese Lösung ist nicht kugelsicher, funktioniert jedoch je nach Maßstab.

Wurzel
quelle