'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.



Solution 1:[1]

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.

Solution 2:[2]

Not sure if you like this any better.

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

Some other ideas. If actually doesn't check the exit code of a command. But if a command has no output when failing, and has output when successful, it can work. In this case, I'm hiding the error messages. If will always hide a command's regular output.

if(test-connection -Count 1 microsoft.com 2>$null) { 'yes' } # never responds
if(test-connection -Count 1 yahoo.com 2>$null) { 'yes' }
yes

Test-connection happens to have a quiet option that returns a boolean anyway:

if(test-connection -count 1 yahoo.com -quiet) { 'yes' }
yes
if(test-connection -count 1 microsoft.com -quiet) { 'yes' }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2