Подтвердить что ты не робот

Захват программы stdout и stderr для разделения переменных

Можно ли перенаправить стандартный вывод из внешней программы в переменную, а стандартный вывод из внешних программ в другую переменную за один прогон?

Например:

$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!";
}

Скучный пример, однако мне интересно, возможно ли это?

4b9b3361

Ответ 1

Самый простой способ сделать это - использовать файл для вывода stderr, например:

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

Я бы также использовал $LastExitCode для проверки ошибок из собственных консольных exes.

Ответ 2

Один из вариантов - объединить вывод stdout и stderr в один поток, а затем фильтровать.

Данные из stdout будут строками, а stderr создает объекты System.Management.Automation.ErrorRecord.

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

Ответ 3

Вы должны использовать Start-Process с параметрами -RedirectStandardError -RedirectStandardOutput. Этот другой пост имеет отличный пример, как это сделать (взятый из этой статьи ниже):

$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

Ответ 4

Это также альтернатива, которую я использовал для перенаправления stdout и stderr командной строки при показе вывода во время выполнения 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

Еще одна возможность дополнить то, что уже было дано.

Помните, что это может не всегда работать в зависимости от того, как вызывается script, у меня были проблемы с -OutVariable и -ErrorVariable при вызове из стандартной командной строки, а не в командной строке PowerShell:

PowerShell -File ".\FileName.ps1"

Альтернативой, которая, по-видимому, работает в большинстве случаев, является следующее:

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

К сожалению, вы потеряете вывод в командной строке во время выполнения script и должны будут Write-Host $stdOutAndError после того, как команда вернется, чтобы сделать ее "частью записи" (например, часть командного файла Jenkins бег). И, к сожалению, он не разделяет stdout и stderr.