PowerShell - Prozesse mit Timeout und Ausgabe verwalten

Datum

Es gibt in Powershell die Möglichkeit Prozesse zu starten. Das ist nichts neues. Es gibt auch eine Möglichkeit auf Exit zu warten. Auch das ist nicht neu. Man kann hier auch einen Timeout setzen. Soweit, so gut. Alles schon bekannt.

Was allerdings etwas aufwendiger ist, ist das Ganze mit Verarbeitung der Ausgabe eines solchen Prozesses so hinzubekommen, dass der Timeout auch berücksichtigt wird und man nicht mit Deadlocks endet. Deadlock bedeutet, dass eine Ausgabe nicht abgeschlossen wird, und deshalb der Leseprozess kein Ende findet. Die Anwendung (in diesem Fall Powershell) hängt sich also fest.

Meine Recherchen führten zu unzähligen Beiträgen, wie das wohl am Besten zu lösen sei. Es gibt allerdinsg einen relativ einfachen Weg: Die Ausgabe nicht direkt in eine Variable einzulesen, sondern in eine Datei umzuleiten, und die Datei dann anschließend auszulesen.

Hier eine Beispielfunktion, wie man die Ausgabe von Prozessen direkt in eine Variable einliest und das Ganze mit Timeout versieht (Deadlocks möglich!):

function ProcessTimeout($proc,$arg,$timeout=10000) {
    $pinfo=New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName=$proc
    $pinfo.RedirectStandardError=$false
    $pinfo.RedirectStandardOutput=$true
    $pinfo.UseShellExecute=$false
    $pinfo.Arguments=$arg
    $p=New-Object System.Diagnostics.Process
    $p.StartInfo=$pinfo
    $p.Start() | Out-Null
    $stdout=$p.StandardOutput.ReadToEnd()
    if($p.WaitForExit($timeout) -ne $true) {
        $p.kill()
        return $false
    }
    return $stdout.Split("`n")
}

Sieht cool aus, funktioniert auch, wenn man z. B. Notepad öffnet und einfach nichts tut (Notepad schließt sich dann nach Ablauf des timeouts). Aber das Ganze funktioniert nicht z. B. bei einem Dauerping (ping -t). Dann hängt sich Powershell auf.

Eine mögliche Lösung sieht so aus:

function ProcessTimeout($proc,$arg,$timeout=10000) {
    do {
        $r=Get-Random -Maximum 24000 -Minimum 23
        $tf="C:\Temp\$r.txt"
    }
    while(Test-Path $tf)
    $p=Start-Process -FilePath $proc -ArgumentList $arg -RedirectStandardOutput $tf -NoNewWindow -Passthru
    $to=$null
    $p | Wait-Process -Timeout $timeout -ea 0 -ev $to
    $ret=Get-Content $tf
    del $tf
    if($to) {
        $p | kill
        return $false
    }
    $ret=Get-Content $tf
    del $tf
    return $ret
}

Hier wird eine zufallsgeneriert nummerierte Textdatei unter C:\Temp angelegt, wohin ide Ausgabe umgeleitet wird. Nachdem der Prozess beendet wurde, wird ihr Inhalt in eine Variable eingelesen und die Datei wieder gelöscht. Und das Ganze ohne, dass sich Powershell aufhängt.

Autor
Kategorien Scripting, Windows

PRTG Map