21

Invoke-Expression will return all the text of the command being invoked.

But how can I get the system return value of whether this command has been executed successfully or with a failure? In CMD I could use %errorlevel% to get external command execution state. What about PowerShell?

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
vik santata
  • 2,989
  • 8
  • 30
  • 52

5 Answers5

26

Normally you would use $? to inspect the status of the last statement executed:

PS C:\> Write-Output 123 | Out-Null; $?
True
PS C:\> Non-ExistingCmdlet 123 | Out-Null; $?
False

However, this won't work with Invoke-Expression, because even though a statement inside the expression passed to Invoke-Expression may fail, the Invoke-Expression call it self will have succeeded (ie. the expression, although invalid/non-functional was invoked none the less)


With Invoke-Expression you'll have to use try:

try {
    Invoke-Expression "Do-ErrorProneAction -Parameter $argument"
} catch {
    # error handling go here, $_ contains the error record
}

or a trap:

trap {
    # error handling goes here, $_ contains the error record
}
Invoke-Expression "More-ErrorProneActions"

The alternative is the append ";$?" to the expression you want to invoke:

$Expr  = "Write-Host $SomeValue"
$Expr += ';$?'

$Success = Invoke-Expression $Expr
if(-not $Success){
    # seems to have failed
}

but relies on there not being any pipeline output

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
12

If the executable called by Invoke-Expression supports it, you can use $LASTEXITCODE. You have to be careful about variable scoping, though.

function foo 
{
    $global:LASTEXITCODE = 0 # Note the global prefix.
    Invoke-Expression "dotnet build xyz" # xyz is meaningless, to force nonzero exit code.
    Write-Host $LASTEXITCODE
}

foo

If you run it, the output will be:

Microsoft (R) Build Engine version 15.9.20+g88f5fadfbe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1009: Project file does not exist.
Switch: xyz
1

Observe the 1 at the end denoting nonzero exit code.

If you would forget the global: prefix, instead the output would have 0. I believe this is because your function-scoped definition of LASTEXITCODE would hide the globally-set one.

Konrad Jamrozik
  • 3,254
  • 5
  • 29
  • 59
11

In PowerShell you can evaluate execution status by inspecting the automatic variables

$?
   Contains True if last operation succeeded and False otherwise.

and/or

$LASTEXITCODE
   Contains the exit code of the last Win32 executable execution.

The former is for PowerShell cmdlets, the latter for external commands (like %errorlevel% in batch scripts).

Does this help you?

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Ruts
  • 286
  • 1
  • 18
  • 8
    `Invoke-Expression` always sets `$?` to `$true` no matter what, so `$?` won't help in that case. – ForNeVeR Oct 04 '17 at 07:04
  • 1
    `$?` works for my invocation like `&'path/to/exe' -params`, it returns `True` for exit code 0 and `False` for any other exit code. – CodeManX Jul 06 '18 at 13:19
7

I found a simple way to do it, which keeps STDOUT in tact.

$Expr="MY EXPRESSION"
$Expr += '; $Success=$?'
Invoke-Expression $Expr

Success is now True or False, and all IO remains intact.

cwingrav
  • 949
  • 1
  • 10
  • 10
0

$LASTEXITCODE cannot be used with Invoke-Expression, as it will be zero regardless of whether the expression invoked succeeds or fails:

PS C:\Users\myUserAccount> touch temp.txt
PS C:\Users\myUserAccount> Invoke-Expression "Remove-Item .\temp.txt"
PS C:\Users\myUserAccount> echo $LASTEXITCODE
0

PS C:\Users\myUserAccount> Invoke-Expression "Remove-Item .\temp.txt"
Remove-Item : Cannot find path 'C:\Users\myUserAccount\temp.txt' because it does not 
exist.
At line:1 char:1
+ Remove-Item .\temp.txt
+ ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\myUserAccount\temp.txt:String) [Remove-Item], ItemNotFoundException
   + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand

PS C:\Users\myUserAccount> echo $LASTEXITCODE
0