web-dev-qa-db-fra.com

Capturer le programme stdout et stderr pour séparer les variables

Est-il possible de rediriger stdout d'un programme externe vers une variable et stderr de programmes externes vers une autre variable en une seule fois?

Par exemple:

$global:ERRORS = @();
$global:PROGERR = @();

function test() {
    # Can we redirect errors to $PROGERR here, leaving stdout for $OUTPUT?
    $OUTPUT = (& myprogram.exe 'argv[0]', 'argv[1]');

    if ( $OUTPUT | select-string -Pattern "foo" ) {
        # do stuff
    } else {
        $global:ERRORS += "test(): oh noes! 'foo' missing!";
    }
}

test;
if ( @($global:ERRORS).length -gt 0 ) {
    Write-Host "Script specific error occurred";
    foreach ( $err in $global:ERRORS ) {
        $Host.ui.WriteErrorLine("err: $err");
    }
} else {
    Write-Host "Script ran fine!";
}

if ( @($global:PROGERR).length -gt 0 ) {
    # do stuff
} else {
    Write-Host "External program ran fine!";
}

Un exemple ennuyeux mais je me demande si c'est possible?

36
dusz

La façon la plus simple de le faire est d'utiliser un fichier pour la sortie stderr, par exemple:

$output = & myprogram.exe 'argv[0]', 'argv[1]' 2>stderr.txt
$err = get-content stderr.txt
if ($LastExitCode -ne 0) { ... handle error ... }

J'utiliserais également $ LastExitCode pour vérifier les erreurs des fichiers EXE de la console native.

12
Keith Hill

Une option consiste à combiner la sortie de stdout et stderr en un seul flux, puis à filtrer.

Les données de stdout seront des chaînes, tandis que stderr produit des objets System.Management.Automation.ErrorRecord.

$allOutput = & myprogram.exe 2>&1
$stderr = $allOutput | ?{ $_ -is [System.Management.Automation.ErrorRecord] }
$stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
34
Aaron Schultz

Vous devez utiliser Start-Process avec les options -RedirectStandardError -RedirectStandardOutput. This other post a un excellent exemple de la façon de procéder (extrait de ce post ci-dessous):

$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
8
AckSynFool

C'est également une alternative que j'ai utilisée pour rediriger stdout et stderr d'une ligne de commande tout en affichant la sortie lors de l'exécution de PowerShell:

$command = "myexecutable.exe my command line params"

Invoke-Expression $command -OutVariable output -ErrorVariable errors
Write-Host "STDOUT"
Write-Host $output
Write-Host "STDERR"
Write-Host $errors

C'est juste une autre possibilité de compléter ce qui a déjà été donné.

Gardez à l'esprit que cela peut ne pas toujours fonctionner selon la façon dont le script est appelé. J'ai rencontré des problèmes avec -OutVariable et -ErrorVariable lorsqu'ils ont été appelés à partir d'une ligne de commande standard plutôt que d'une ligne de commande PowerShell comme celle-ci:

PowerShell -File ".\FileName.ps1"

Une alternative qui semble fonctionner dans la plupart des circonstances est la suivante:

$stdOutAndError = Invoke-Expression "$command 2>&1"

Malheureusement, vous perdrez la sortie de la ligne de commande lors de l'exécution du script et devrez Write-Host $stdOutAndError après le retour de la commande pour en faire "une partie de l'enregistrement" (comme une partie d'un fichier batch Jenkins exécuté). Et malheureusement, il ne sépare pas stdout et stderr.

3
James Eby