Home > Back-end >  In Powershell, how to set $? without sending this value to the pipeline?
In Powershell, how to set $? without sending this value to the pipeline?

Time:12-23

PS> gci C:\
<<here it doesn't print the value of $? as 'true' after command executes>

PS> $?
true

However when I try to recreate this behavior with my own powershell function I get:

PS> function test1 { write-host "hello"; return $true}

PS> test1
hello
true

PS> $?
true

What gives? why can't I hide the return value of the function from the pipeline, and have it only written to the $? variable?

CodePudding user response:

This is one of many automatic variables in PowerShell, you can see the full list by running:

get-help about_Automatic_Variables

The function of the $? is to see if the last command ran to completion or encountered an uncaught error.

For instance

function Throw-WhenOdd($value){
   if ($value % 2 -ne 0){
      throw "OddNumber"
   }
}


PS> Throw-WhenOdd 4
PS> $?
True
PS> Throw-WhenOdd 3
Exception:
Line |
   3 |        throw "OddNumber"
     |        ~~~~~~~~~~~~~~~~~
     | OddNumber
PS>$?
False
PS> #Checking the value of `$?` resets the value
# so checking a second time returns true
PS> $?
true

So to summarize, if you have a function or cmdlet or whatever and it can throw, you can use $? to see if it threw or not. However, you have to use Throw and not Write-Error and the output is consumed the first time you check $?.

CodePudding user response:

First, to clarify:

  • In PowerShell, return - if given an argument - outputs that argument to the success output stream, i.e. it returns data - just like implicit output or output with Write-Output (whose use is rarely needed) does.

    • Also note that Write-Host is not designed to output data - it is designed for direct-to-host (console) output - see this answer.
  • By contrast, in POSIX-compatible shells such as bash, return is the function-level analog to exit and sets an invisible exit code.

  • PowerShell's $? is an abstract error-condition indicator ($true if no errors occurred, $false otherwise), which is automatically set by PowerShell itself to indicate whether the most recently executed command or expression experienced an error condition - see details below.


  • As of PowerShell 7.2, you cannot set the automatic $? variable; it is set by PowerShell itself - see the bottom section for details.

    • The need for the ability to set $? directly has been recognized and green-lighted in GitHub issue #10917, but has yet to be implemented.
  • You can only set it indirectly:

    • Via exit codes, which only apply to external programs (process exit code) script files (*.ps1) if they terminate with an exit statement.

      • The most recently reported exit code, which is also reflected in the automatic $LASTEXITCODE variable, maps to $? reporting $true, if it is 0, and $false for any nonzero value.
    • For cmdlets and functions, via output to PowerShell's error stream:

      • For binary cmdlets, emitting at least one non-terminating error or a statement-terminating error results in $? reflecting $false

      • As of PowerShell 7.2, (by definition written-in-PowerShell) functions only affect $? if they're advanced functions and use the unwieldy $PSCmdlet.WriteError() (for a non-terminating error) $PSCmdlet.ThrowTerminatingError() (for a statement-terminating error) methods:

        • Write-Error (for non-terminating errors), unfortunately, does not affect $? - see GitHub issue #3629

        • While a throw statement - as shown in FoxDeploy's answer - does end up setting $? to $false, it generates a script-terminating (runspace-terminating) error, which requires the caller to explicitly catch the error, with try / catch, and $? must then be queried at the start of the catch block.

      • Notably, the above means:

        • Getting $? to reflect $false invariably requires producing error output.
        • $? being $false just tells you that some error occurred - it isn't a reflection of an explicit intent to signal success vs. failure of the command as a whole.
  • Related