Kir
Kir

Reputation: 3042

PS7.1 - How do you use pipeline chaining with custom functions?

According to documentation, PS 7 has introduced pipeline chaining operators such as || and &&. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_pipeline_chain_operators?view=powershell-7

And you should be able to do C#-style short circuiting operations like:

Write-Error 'Bad' && Write-Output 'Second'

The above examples work. And the documentation says that the pipeline chaining operators use two fields (unsure how that works precisely): $? and $LASTEXITCODE

How do I apply these to my own functions?

For example:

function yes() { 
  echo 'called yes'
  return $True 
}
function no() { 
  echo 'called no'
  return $False 
}

I feel like I should be able to run the following no && yes and see the following output

called no

False

but instead I see

called no

False

called yes

True

So how do I develop functions in such a way that I can use pipeline chaining and short circuiting?

edit: the only way I can figure out right now to construct a custom function that will short circuit an && is to make it throw, but that doesn't seem too useful in the general case.

Upvotes: 2

Views: 128

Answers (1)

mklement0
mklement0

Reputation: 437080

&& and || solely operate on the success status of a command, as reflected in the automatic $? variable (as you state) - which is unrelated to what a command outputs (returns).

Functions and scripts report $? as $true, unless explicit action is taken; that is, even if commands used inside the script / function fail, $? is still $true when the script / function exits.

Unfortunately, as of PowerShell 7.0, there is no direct way for a function to directly set $? to $false, although the plan is to add such a feature - see this GitHub isssue.

In a script, using exit with a nonzero exit code is effective, however (this causes $LASTEXITCODE to reflect the exit code, and the PowerShell engine sets $? to $false if the exit code is nonzero - this is also how it works when you call external programs).


For now, there's only the following suboptimal workaround for functions; it is suboptimal in that it invariably emits an error message:

function no { 
  # Make the function an advanced one, so that $PSCmdlet.WriteError()
  # can be called.
  [CmdletBinding()]
  param()

  # Call $PSCmdlet.WriteError() with a dummy error, which
  # sets $? to $false in the caller's scope.
  # By default, this dummy error prints and is recorded in the $Error
  # collection; you can use -ErrorAction Ignore on invocation to suppress
  # that.
  $PSCmdlet.WriteError(
   [System.Management.Automation.ErrorRecord]::new(
     [exception]::new(), # the underlying exception
     'dummy',            # the error ID
     'NotSpecified',     # the error category
     $null)              # the object the error relates to
  ) 
}

Function no now sets $? to false, which triggers a || branch; -EA Ignore (-ErrorAction Ignore) is used to silence the dummy error.

PS> no -EA Ignore || 'no!'
no!

Upvotes: 1

Related Questions