Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a powershell pattern for if($?) { }

I find myself chaining these a lot, eg:

do-cmd-one
if($?)
{
do-cmd-two
}
...

then at the end:

if(!$?)
{
   exit 1
}

I assume there's a pattern for this in powershell but I don't know it.

like image 644
Carbon Avatar asked Dec 08 '22 11:12

Carbon


1 Answers

PowerShell (Core) 7.0 introduced Bash-like && and || operators called pipeline-chain operators.

They will not be back-ported to Windows PowerShell, however, as the latter will generally see no new features.

In short, instead of:

do-cmd-one; if ($?) { do-cmd-two }

Note: Up to PowerShell 7.1, the more robust formulation is actually
do-cmd-one; if ($LASTEXITCODE -eq 0) { do-cmd-two }, for the reasons explained in this answer.

you can now write:

do-cmd-one && do-cmd-two

&& (AND) and || (OR) implicitly operate on each command's implied success status, as reflected in automatic Boolean variable $?.

This will likely be more useful with external programs, whose exit codes unambiguously imply whether $? is $true (exit code 0) or $false (any nonzero exit code).

By contrast, for PowerShell commands (cmdlets) $? just reflects whether the command failed as a whole (a statement-terminating error occurred) or whether at least one non-terminating error was reported; the latter doesn't necessarily indicate overall failure.
However, there are plans to allow PowerShell commands to set $? directly, as a deliberate overall-success indicator.

Also note that the following do not work with && and ||:

  • PowerShell's Test-* cmdlets, because they signal the test result by outputting a Boolean rather than by setting $?; e.g.,
    Test-Path $somePath || Write-Warning "File missing" wouldn't work.

  • Boolean expressions, for the same reason; e.g.,
    $files.Count -gt 0 || write-warning 'No files found' wouldn't work.

See this answer for background information, and the discussion in GitHub issue #10917.


There's a syntax caveat: As of this writing, the following will not work:

do-cmd-one || exit 1 # !! Currently does NOT work

Instead, you're forced to wrap exit / return / throw statements in $(...), the so-called subexpression operator:

do-cmd-one || $(exit 1) # Note the need for $(...)

GitHub issue #10967 discusses the reasons for this awkward requirement, which are rooted in the fundamentals of PowerShell's grammar.

like image 66
mklement0 Avatar answered Dec 15 '22 21:12

mklement0