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.
2 Answers
To complement Martin Brandl's helpful answer with more detailed information:
tl;dr
Automatic variable
$?(seeGet-Help about_Automatic Variables) contains a Boolean that reflects whether any non-terminating error occurred in the most recent statement.- Since
$?is set after every statement, you must either check it right after the statement of interest, or save it for later inspection. - See below for potentially counter-intuitive behavior.
- Since
Automatic variable
$LASTEXITCODEcomplements this by recording the specific exit code of the most recently executed external command-line utility (console application, such asfindstr).$LASTEXITCODEcomplements$?in that$?reflects only abstract success or failure of the external utility - exit code0is mapped to$True, any nonzero exit code to$False- whereas$LASTEXITCODEcontains the actual exit code.- Since
$LASTEXITCODEis 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$falseeven when$LASTEXITCODEis0, namely when a2>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 usetry / catch(preferred) ortrap(see theGet-Help about_Try_Catch_FinallyandGet-Help about_Trap).- Conversely, you can opt to have non-terminating errors treated as terminating ones, using preference variable
$ErrorActionPreferenceor common cmdlet parameter
-ErrorAction(alias-EA) - seeGet-Help about_Preference_VariablesandGet-Help about_CommonParameters.
- Conversely, you can opt to have non-terminating errors treated as terminating ones, using preference variable
Unless explicitly ignored (with the common
-ErrorAction Ignorecmdlet parameter), all non-terminating errors (and caught terminating errors) are collected in the automatic$Errorcollection, in reverse chronological order; that is, element$Error[0]contains the most recent error.For commands to which multiple input objects were passed,
$?containing$Falseonly 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.- To determine the exact error count and the offending input objects, you must examine the
$Errorcollection.
- To determine the exact error count and the offending input objects, you must examine the
With non-remoting indirect-execution cmdlets to which you pass a target command to execute - such as
Invoke-Expression,Start-ProcessandStart-JobandInvoke-Commandwithout the-ComputerNameparameter (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.- A simple example:
Invoke-Expression '1 / 0'sets$?to$True(!), becauseInvoke-Expressionwas able to parse and invoke the expression, even though the expression itself then fails. - Again, inspecting the
$Errorcollection tells you if and what errors the target command reported.
- A simple example:
With remoting (invariably indirect-execution) cmdlets, notably with
Invoke-Commandwith the-ComputerNameparameter (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$Falseto reflects the failure of target command1 / 0.
Note that even though the local computer (.) is targeted, use of-ComputerNameinvariably 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 }
- 382,024
- 64
- 607
- 775
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.
- 56,134
- 13
- 133
- 172
-
1Thanks Martin for the chart, its succent. In the chart, at the bottom, I got this [link](https://technet.microsoft.com/en-us/library/hh847768.aspx), guess this is official documentation. – VivekDev Oct 05 '16 at 05:11
-
1Yes, thats the official documentation. Your welcome. – Martin Brandl Oct 05 '16 at 05:13