I am writing some PowerShell scripts to do some build automation. I found here that echo $? returns true or false depending on previous statement. I just found that echo is alias for Write-Output. Write-Host $? also works. But I am still not clear how this $? works. Can someone kindly say some words bout this. Searching for echo $? on net did not give me much.
To complement Martin Brandl's helpful answer with more detailed information:
tl;dr
Automatic variable $? (see Get-Help about_Automatic Variables) contains a Boolean that reflects whether any non-terminating error occurred in the most recent statement.
$? is set after every statement, you must either check it right after the statement of interest, or save it for later inspection.Automatic variable $LASTEXITCODE complements this by recording the specific exit code of the most recently executed external command-line utility (console application, such as findstr).
$LASTEXITCODE complements $? in that $? reflects only abstract success or failure of the external utility - exit code 0 is mapped to $True, any nonzero exit code to $False - whereas $LASTEXITCODE contains the actual exit code.$LASTEXITCODE is only set for external command-line utilities, its value typically remains valid longer than $?, which is set after every statement.There are many subtleties around how $? is set, and what, precisely, its value indicates:
In PowerShell versions prior to v7.2, $? there is a risk of false negatives for external command-line utilities, i.e. it can report $false even when $LASTEXITCODE is 0, namely when a 2> redirection is used and actual stderr output is present - see this answer for details.
$? only reflects the occurrence of nonterminating errors, because (the much rarer) terminating errors by default terminate execution of the current command line / script, and to handle them you need to use try / catch (preferred) or trap (see the Get-Help about_Try_Catch_Finally and Get-Help about_Trap).
$ErrorActionPreference or common cmdlet parameter-ErrorAction (alias -EA) - see Get-Help about_Preference_Variables and Get-Help about_CommonParameters.Unless explicitly ignored (with the common -ErrorAction Ignore cmdlet parameter), all non-terminating errors (and caught terminating errors) are collected in the automatic $Error collection, in reverse chronological order; that is, element $Error[0] contains the most recent error.
For commands to which multiple input objects were passed, $? containing $False only tells you that processing of at least one input object failed. In other words: an error could have occurred for any subset of the input objects, including all of them.
$Error collection.With non-remoting indirect-execution cmdlets to which you pass a target command to execute - such as Invoke-Expression, Start-Process and Start-Job and Invoke-Command without the -ComputerName parameter (which doesn't involve remoting - see below) - $? only reflects whether the target command could be invoked in principle, irrespective of whether that command then reports errors or not.
Invoke-Expression '1 / 0' sets $? to $True(!), because Invoke-Expression was able to parse and invoke the expression, even though the expression itself then fails.$Error collection tells you if and what errors the target command reported.With remoting (invariably indirect-execution) cmdlets, notably with Invoke-Command with the -ComputerName parameter (as is typical), but also with implicitly remoting cmdlets, $? does reflect whether the target command reported any errors.
A simple example (must be run from an elevated console and assumes that the local machine is already set up for remoting):Invoke-Command -ComputerName . { 1 / 0 }, because remoting is involved, indeed sets $? to $False to reflects the failure of target command 1 / 0.
Note that even though the local computer (.) is targeted, use of -ComputerName invariably uses remoting.
Note that, by design, remoting reports normally terminating errors that occur remotely as non-terminating ones, presumably so that a normally terminating error on one target machine doesn't abort processing on all others.
Examples of commands that DO reflect errors in $?:
# Invoking a non-existing cmdlet or utility directly.
NoSuchCmd
# Ditto, via call operator &.
# Note, however, that using a *script block* with & behaves differently - see below.
& 'NoSuchCmd'
# Invoking a cmdlet with invalid parameter syntax.
Get-ChildItem -NoSuchParameter
# Invoking a cmdlet with parameter values that cause a (non-terminating) runtime error.
Get-ChildItem NoSuchFile
# Invoking an external utility that reports a nonzero exit code.
findstr -nosuchoptions
# The specific exit code is recorded in $LASTEXITCODE,
# until the next external utility is called.
# Runtime exceptions
1 / 0
# A cmdlet that uses remoting:
# (Must be run from an elevated session, and the local machine must
# be configured for remoting first - run `winrm quickconfig`).
# Note that remoting would NOT be involved WITHOUT the -ComputerName parameter,
# in which case `$?` would only reflect whether the script block could be
# _invoked_, irrespective of whether its command(s) then fail or not.
Invoke-Command -ComputerName . { 1 / 0 }
# A .NET method that throws an exception.
# Note: Outside of a `try/catch` handler, this is a non-terminating error.
# Inside a `try/catch` handler, .NET exceptions are treated as terminating
# and trigger the `catch` block.
[System.IO.Path]::IsPathRooted('>')
Examples of commands that DO NOT reflect errors in $?:
<#
Non-remoting indirect execution cmdlets:
$? reflects only whether the specified command could be
*invoked*, irrespective of whether the command itself then failed or not.
In other words: $? is only $False if the specified command could not even be
executed, such as due to invalid parameter syntax, an ill-formed target
command, or a missing target executable.
#>
# Invoking a command stored in a script block.
& { 1 / 0 }
# Invoking an expression stored in a string.
Invoke-Expression '1 / 0'
# Starting a background job.
Start-Job { 1/ 0 }
# The *non-remoting* form of Invoke-Command (WITHOUT -ComputerName).
Invoke-Command { 1 / 0 }
You find a complete Punctuation chart here. The answer (taken from the chart):
Execution status of the last operation ($true or $false); contrast with $LastExitCode that reports the exit code of the last Windows-based program executed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With