2

I would like to invoke an arbitrary expression and redirect std error to a variable without redirection to a file.

For example, in Powershell it is possible to redirect standard error using 2> operator. Using a temporary file, I can easily get what I want:

#$expr = "1/0"                          # with std error
#$expr = "2+2"                          # without stderror
#$expr = "Get-Service invalidsvc"       # with stderror
$expr = "try { throw '111' } catch { }" # without std error
$ans = Invoke-Expression $expr -ErrorVariable ev 2> C:\log\stderr.txt
if(cat C:\log\stderr){
    Write-Host "Error $ev"    
}

How can I do the same, but without creation of a temporal output file?

Wrong solutions:

  1. Using -ErrorVariable switch. Counter example:

    Invoke-Expression $expr -ErrorVariable ev 2> C:\aaa\file1.txt $expr = "try { throw '111' } catch { }" Write-Host "Error $ev" # will output error, but std error is empty

  2. $LASTEXITCODE and $? check. Counter example:

    Get-Service invalidservice

$lastexitcode is equal to 0, $? is equal to True, but std error is not empty

The idea is simple: save the "red" (std error) text in Powershell console in a variable. The command I receive is an arbitrary string. Examples:

  1. When I write "try { throw '111' } catch { }" in Powershell console there will be no red (error) text in PS console (despite the fact $error is not empty). So if I invoke that expression in my code I get no error saved in some variable.

  2. When I write "Get-Service notexistingservice", or "1/0", or "Write-Error 111" there will red (error) text and non-null $error. So if I invoke that expression in my code I would like to get error saved in some variable.

3 Answers 3

8

Save standard output and standard error to separate variables. It won't work without the dollar sign (from Windows Powershell in Action).

$err = $( $output = get-childitem foo3 ) 2>&1
Sign up to request clarification or add additional context in comments.

2 Comments

In my case $? is always true including when the executed command failed. Any idea how to solve that?
@glihm What command? That's very unusual unless the command is running in the background. Maybe you should post a new question.
1

The way to do it is the -errorvariable common parameter. Your counter example is only valid (and I only hesitantly use that word) because you have explicitly coded for it to not output an error with the use of the Try/Catch and not including anything coding in your catch. You are basically complaining that you told PowerShell to send error cases to the Catch scriptblock, where you did not output anything, and then having an issue when nothing is output. An error still occurs, it is logged in the errorvariable as you stated it should be, and also stored in $Error, but since you did not output anything in your Catch block there's nothing for your StdErr redirect to do.

If you want $ev to not contain an error because you corrected the issue in your Catch block, then also clear the variable in the catch block.

$expr = 'try{ throw "1111"}catch{Remove-Variable ev}'

Or if you want StdErr to contain the error text, make sure you include that output in your Catch block:

$expr = 'try{ throw "1111"}catch{Write-Error $_.Exception.Message}'

2 Comments

I updated the content of the post to make thing round. Your code, of course, will work, but not for arbitrary expression, i.e. I have no info about expression I execute.
another words, how can I differentiate "try { throw 111; } catch{}" and simply "throw 111"? They both have non-empty $error variable. The only thing I can use is $?, but it does not work with "Get-service aaaa".
0

I know this is a very old question but I had the same problem and wanted to share what I ended up with.

        $error.clear()
    $List = New-Object PSObject
    Invoke-Command -ComputerName $server -ScriptBlock {
        $smbv1 = (Get-SmbServerConfiguration | Select EnableSMB1Protocol)
        $smbv1 | Select-Object EnableSMB1Protocol} -OutVariable List    

    foreach($item in $list.EnableSMB1Protocol){
        if($error -ne $null){
            $item = "unknown"
            $msg = $error.Exception.Message
            ac .\smb1errors.txt "$Server, $msg"
        }

Since the $error variable is a .NET object in order to clear it I needed to pass it parameter (). I then executed my code, in this case checking for SMBv1 service, and testing if $error is still $null. If the $error variable has content I grabed it in a variable. $msg = $error.Exception.Message

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.