Start-Job, AsJob: PowerShell-Kommandos im Hintergrund ausführen

    PowerShell Konsole IconBefehle oder Scripts, deren Abar­beitung länger dauert, kann Power­Shell in den Hin­ter­grund schicken. Damit bleibt die Kom­mando­zeile frei für weitere Ein­gaben und der Benutzer kann die Ergeb­nisse nach Ab­schluss des Jobs abrufen. Dies funk­tioniert auch mit der Aus­führung von Be­fehlen auf Remote-PCs.

    Grundsätzlich ist es keine neue Idee, Kommandos in einer interaktiven Shell im Hintergrund zu starten, so dass man in der Zwischenzeit weiterhin Zugriff auf den Prompt hat. Während dieses Konzept unter Unix schon lange realisiert wurde, kannte der alte Kommando­interpreter aber nichts Vergleichbares. PowerShell hingegen bietet ein umfangreiches Instrumentarium für das Job-Management.

    Mehrere Varianten zum Starten von Jobs

    Viele Cmdlet sehen den Start als Hintergrund-Job von Haus aus vor, indem sie den Parameter AsJob unterstützen. Sie benötigen somit kein externes Kommando, um diesen Modus zu aktivieren. Wenn man wissen möchte, welche Cmdlets diese Option vorsehen, dann kann man dies folgender­maßen herausfinden:

    Get-Command -ParameterName AsJob

    Unter Windows 10 mit installiertem Hyper-V-Modul gibt dieser Befehl über 200 Cmdlets aus.

    Möchte man etwa eine oder mehrere VMs im Hintergrund starten, anstatt die Fortschritts­anzeige zu betrachten, dann könnte man dies so tun:

    Start-VM -Name win10pro-vm2 -AsJob

    Dieser Verfahren klappt genauso, wenn man eine interaktive Remote-Session mit Enter-PSSession aufgebaut hat. Der Unterschied besteht nur darin, dass die Befehle des Jobs auf dem Remote-Host laufen.

    Eine weitere Variante, um Remote-Befehle im Hintergrund auszuführen, ergibt sich, wenn ein Cmdlet zusätzlich den Parameter ComputerName unterstützt:

    Stop-VM -ComputerName win81-i7-L1 -Name WS2016-VM1 -AsJob

    Start-Job

    Möchte man ein Script im Hintergrund starten oder ein Cmdlet, das den Schalter AsJob nicht vorsieht, dann hilft hier Start-Job:

    Start-Job -ScriptBlock {Get-Process -Name *Power*}

    Die auszuführenden Befehle bzw. Scripts müssen dem Cmdlet in geschweiften Klammern übergeben werden, der Parameter­name ScriptBlock ist dabei optional. Diese Methode eignet sich, um einen Hintergrund-Job lokal zu starten oder in einer interaktiven Remote-Sitzung, die man mit Enter-PSSession geöffnet hat.

    PowerShell-Kommando im Hintergrund ausführen mit Start-Job.

    Will man jedoch einen Job remote starten, ohne explizit eine Session mit einem Host aufzubauen, dann erfüllt Invoke-Command im Zusammen­spiel mit dem Schalter AsJob diese Aufgabe:

    Invoke-Command -Computername WS2016-VM1 -ScriptBlock {Get-Process -Name *Power*} -AsJob

    Das Ergebnis dieses Jobs gelangt schließlich zurück an den lokalen Rechner.

    Status von Jobs überwachen

    Unabhängig davon, wie man einen Befehl oder ein Script im Hintergrund startet, gibt PowerShell einige wesentliche Infor­mationen des Jobs am Bildschirm aus. Dazu gehören vor allem die ID, der Status und Rechner, auf dem er läuft.

    Alternativ kann man diese Daten in einer Variablen speichern, indem man diese die Referenz auf das Job-Objekt zuweist. In diesem Fall erscheinen die genannten Informa­tionen nicht auf Stdout und das Management des Jobs erfolgt dann über die betreffende Variable:

    $MyJob = Start-Job -ScriptBlock {Get-Process -Name *Power*}

    Will man später Einblick in den Fortschritt eines Jobs gewinnen, dann dient Get-Job diesem Zweck. Ohne Parameter zeigt er sämtliche Jobs dieser Session an. Alternativ übergibt man dem Cmdlet entweder die ID oder den Namen eines oder mehrerer Jobs:

    Get-Job -ID 29

    Hat man die Referenz zum Job-Objekt in einer Variablen gespeichert, dann gibt man einfach ihren Inhalt aus.

    Darüber hinaus kann man die Job-Liste anhand verschiedener Eigenschaften filtern. So erlauben die Parameter HasMoreData oder State die Ausgabe jener Jobs, von denen noch Ergebnisse abzuholen sind bzw. die einen bestimmten Status wie Completed oder Stopped aufweisen.

    Ergebnisse von Hintergrund-Jobs abrufen

    Die meisten Kommandos liefern ein Ergebnis, das man nach Beendigung des Jobs abholen möchte. Dieser Part kommt dem Cmdlet Receive-Job zu. In der einfachsten Form übergibt man ihm die ID oder den Namen den Jobs, alternativ über den Parameter Job die Referenz auf das Objekt, das man in einer Variablen gespeichert hat:

    $job = Invoke-Command -Computername WS2016-VM1 -ScriptBlock {Get-Process} -AsJob

    Receive-Job -Job $job

    Mit diesem Kommando erhält man die Daten, die das Script bzw. die Befehle im ScriptBlock bis zu diesem Zeitpunkt generiert haben. Standardmäßig löscht Receive-Job diese an der Quelle, so dass man beim nachfolgenden Aufruf bloß die nächste Tranche der Daten erhält, die man dann Stück für Stück zusammenführen muss.

    Daten eines Hintergrund-Jobs abholen mit Receive-Job.

    Wenn man das nicht möchte, dann sorgt der Schalter Keep dafür, dass bei jedem Abruf mittels Receive-Job sämtlicher bis dahin produzierter Output zurückgegeben wird:

    Receive-Job -ID 19 -keep

    Entsprechend bleibt die Eigenschaft HasMoreData des Jobs auf True.

    Alternativ dazu kann man Receive-Job mit dem Schalter Wait dazu zwingen, solange zu warten, bis der Job voll­ständig abgearbeitet ist. In diesem Fall blockiert das Cmdlet jedoch den Prompt, was letztlich nicht im Sinne von Hintergrund-Jobs ist.

    Jobs anhalten oder löschen

    Wenn sich herausstellt, dass ein Script zu lange im Hintergrund läuft, weil es möglichweise wegen eines Programmierfehlers seinen Auftrag nie erfüllen wird, dann kann man es anhalten oder löschen.

    Die erste Option kommt vor allen dann in Betracht, wenn man die bis zum jeweiligen Zeitpunkt erzeugten Daten noch mittels Receive-Job abholen möchte. In diesem Fall ruft man das Cmdlet Stop-Job auf, wobei man den Job wieder über die ID angeben kann.

    PowerShell-Jobs löschen mit Remove-Job.

    Will man Jobs ganz entfernen, dann greift man dafür auf Remove-Job zurück. Auch dieses Cmdlet erwartet die Job-ID. Möchte man alle Jobs abräumen, die bereits gelaufen sind, dann könnte man das so machen:

    Get-Job -State Completed | Remove-Job

    1 Kommentar

    Bild von Julias Reven
    Julias Reven sagt:
    9. Juni 2017 - 13:56

    Guten Tag,

    ist es auch möglich über die Job-ID an einen Fehlerlog zu kommen, falls der Job fehlgeschlagen ist?

    Viele Grüße

    Julias