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?
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
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?
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.
$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