61

In Bash I can easily do something like

command1 && command2 || command3

which means to run command1 and if command1 succeeds to run command2 and if command1 fails to run command3.

What's the equivalent in PowerShell?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Andrew J. Brehm
  • 4,448
  • 8
  • 45
  • 70
  • http://stackoverflow.com/questions/1741490/how-to-tell-powershell-to-wait-for-each-command-to-end-before-starting-the-next ? – stej Mar 10 '10 at 12:12
  • 2
    Stej, you did - http://stackoverflow.com/questions/2251622/conditional-execution-and-in-powershell – Keith Hill Mar 10 '10 at 15:08
  • 3
    Interesting method to write your own [over on superuser](http://superuser.com/a/764639/114388). – ruffin Nov 25 '14 at 15:24
  • 4
    **June 2019 update**: PowerShell team are implementing `&&` and `||`! Weigh in at the [GitHub PR](https://github.com/PowerShell/PowerShell/pull/9849) – pilau Jun 12 '19 at 07:52

4 Answers4

59

Update: && and || have finally come to PowerShell (Core), namely in v7+, termed pipeline-chain operators; see this answer for details.


Many years after the question was first asked, let me summarize the behavior of Windows PowerShell, whose latest and final version is v5.1:

  • Bash's / cmd's && and || control operators have NO PowerShell equivalents, and since you cannot define custom operators in PowerShell, there are no good workarounds:

    • Use separate commands (on separate lines or separated with ;), and explicitly test the success status of each command via automatic $? variable, e.g.:

      # Equivalent of &&
      command1 -arg1 -arg2; if ($?) { command2 -arg1 }
      
      # Equivalent of ||
      command1 -arg1 -arg2; if (-not $?) { command2 -arg1 } 
      
    • Better alternative for external programs, using the automatic $LASTEXITCODE variable; this is preferable, because $? in Windows PowerShell can yield false negatives (no longer in PowerShell (Core) 7.2+), if a 2> redirection is involved - see this answer:

      # Equivalent of &&
      command1 -arg1 -arg2; if ($LASTEXITCODE -eq 0) { command2 -arg1 }
      
      # Equivalent of ||
      command1 -arg1 -arg2; if ($LASTEXITCODE -ne 0) { command2 -arg1 } 
      
    • See below for why PowerShell's -and and -or are generally not a solution.

  • [Since implemented in PowerShell (Core) 7+] There was talk about adding them a while back, but it seemingly never made the top of the list.

    • Now that PowerShell has gone open-source, an issue has been opened on GitHub.
    • The tokens && and || are currently reserved for future use in PowerShell, so there's hope that the same syntax as in Bash can be implemented.
      (As of PSv5.1, attempting something like 1 && 1 yields error message The token '&&' is not a valid statement separator in this version.)

Why PowerShell's -and and -or are no substitute for && and ||:

Bash's control operators && (short-circuiting logical AND) and || (short-circuiting logical OR) implicitly check the success status of commands by their exit codes, without interfering with their output streams; e.g.:

ls / nosuchfile && echo 'ok'

Whatever ls outputs -- both stdout output (the files in /) and stderr output (the error message from attempting to access non-existent file nosuchfile) -- is passed through, but && checks the (invisible) exit code of the ls command to decide if the echo command - the RHS of the && control operator - should be executed.

ls reports exit code 1 in this case, signaling failure -- because file nosuchfile doesn't exist -- so && decides that ls failed and, by applying short-circuiting, decides that the echo command need not be executed.
Note that it is exit code 0 that signals success in the world of cmd.exe and bash, whereas any nonzero exit code indicates failure.

In other words: Bash's && and || operate completely independently of the commands' output and only act on the success status of the commands.


PowerShell's -and and -or, by contrast, act only on the commands' standard (success) output, consume it and then output only the Boolean result of the operation; e.g.:

(Get-ChildItem \, nosuchfile) -and 'ok'

The above:

  • uses and consumes the success (standard) output -- the listing of \ -- and interprets it as a Boolean; a non-empty input collection is considered $true in a Boolean context, so if there's at least one entry, the expression evaluates to $true.

    • However, the error information resulting from nonexistent file nosuchfile is passed through, because errors are sent to a separate stream.
  • Given that Get-ChildItem \, nosuchfile returns non-empty success output, the LHS evaluated to $true, so -and also evaluates the RHS, 'ok', but, again, consumes its output and interprets it as a Boolean, which, as a nonempty string, also evaluates to $true.

  • Thus, the overall result of the -and expression is $true, which is (the only success) output.

The net effect is:

  • The success output from both sides of the -and expression is consumed during evaluation and therefore effectively hidden.

  • The expression's only (success) output is its Boolean result, which is $true in this case (which renders as True in the terminal).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 2
    Huge hack, but I need an inline call and couldn't add functions. So I ended up with this: `command1 ; $? -or (command3)` – Pedro Witzel Apr 17 '19 at 10:06
  • 1
    Interesting, @PedroWitzel; that would still output a Boolean, however, while suppressing `command3`'s stdout output; to get closer to Bash's behavior, you'd need something like `command1; $null = $? -or ($output = command3); $output`, but that would print stderr output before stdout. – mklement0 Apr 17 '19 at 11:26
  • Are the first two code examples switched? Wouldn't `command1 -arg1 -arg2; if ($?) { command2 -arg1 }` be the equivalent of `||`? – xdhmoore Jan 19 '20 at 23:31
  • No, @xdhmoore: `$?` is a _Boolean_ in PowerShell. – mklement0 Jan 19 '20 at 23:45
12

What Bash must be doing is interpreting the integer exit code of the commands as true or false when passed to the logical operators. PowerShell doesn't do this - but a function can be made to wrap the command and create the same behavior:

> function Get-ExitBoolean($cmd) { & $cmd | Out-Null; $? }

($? is a bool containing the success of the last exit code)

Given two batch files:

#pass.cmd
exit

and

#fail.cmd
exit /b 200

...the behavior can be tested:

> if (Get-ExitBoolean .\pass.cmd) { write pass } else { write fail }
pass
> if (Get-ExitBoolean .\fail.cmd) { write pass } else { write fail }
fail

The logical operators should be evaluated the same way as in Bash. First, set an alias:

> Set-Alias geb Get-ExitBoolean

Test:

> (geb .\pass.cmd) -and (geb .\fail.cmd)
False
> (geb .\fail.cmd) -and (geb .\pass.cmd)
False
> (geb .\pass.cmd) -and (geb .\pass.cmd)
True
> (geb .\pass.cmd) -or (geb .\fail.cmd)
True
James Kolpack
  • 9,331
  • 2
  • 44
  • 59
  • 24
    So there is no simple built-in functionality for this? – Andrew J. Brehm Mar 10 '10 at 15:15
  • 3
    No, don't think so. I believe the designers did their best to avoid operator-hijacking usage like that. In fact, there's no ternary operator either. It is powerful enough, however, to provide the means to easily roll your own. http://blogs.msdn.com/powershell/archive/2006/12/29/dyi-ternary-operator.aspx – James Kolpack Mar 10 '10 at 15:51
  • 1
    Not sure the `| Out-Null` is necessarily a good idea. Maybe pipe to `Write-Host` so we can still see the output? – jpmc26 Aug 21 '14 at 14:40
  • 2
    @JamesKolpack: The only reason for the absence of both `&&` / `||` and the ternary conditional operator from the language is that _they never made it to the top of the list - both have been acknowledged as potentially useful additions_: see [here](http://stackoverflow.com/a/573295/45375) for `&&` / `||`, and, with respect to the ternary operator, the very article you link to starts with "One of the things we were very disappointed in not being able to ship in V1.0 is a ternary operator." – mklement0 Jan 24 '17 at 04:34
  • 5
    To be clear, `Get-ExitCodeBoolean()` is _not_ a generic replacement for `&&` / `||`, because it _suppresses regular output_, preventing its capture in a variable / file / sending it through a pipeline. The beauty of Bash's `&&` / `||` control operators is that _they don't interfere with the output streams and only act on the (invisible) exit codes_ - and that cannot (currently) be done in PowerShell. If you think it should, [vote for it here](https://windowsserver.uservoice.com/forums/301869-powershell/suggestions/11087898-implement-the-and-operators-that-bash-has). – mklement0 Jan 24 '17 at 04:44
  • "What Bash must be doing is implicitly casting the exit code of the commands to a Boolean when passed to the logical operators." Definitely no, if for no other reason than bash only knows strings and 32 bit integers. – RonJohn May 26 '23 at 20:30
  • @RonJohn - thanks, it's definitely not worded correctly, I removed the "casting" bit and talked about Bash interpreting the integers as true or false when doing logical operations. – James Kolpack May 28 '23 at 19:43
  • @JamesKolpack that’s not true either. **Success** == 0, while **failure** >= 0. – RonJohn May 29 '23 at 01:04
-2

You can do something like this, where you hide the boolean output with [void], and only get the side effect. In this case, if $a or $b are null, then $c gets assigned to $result. An assignment can be an expression.

$a = ''
$b = ''
$c = 'hi'

[void](
  ($result = $a) -or
  ($result = $b) -or
  ($result = $c))

$result

output

hi
js2010
  • 23,033
  • 6
  • 64
  • 66
  • You can't derive a command's success vs. failure from its _output_, so this no substitute for `&&` and `||`. What if a command succeeds and either produces no output at all or happens to output something that is falsy, e.g. `0`? – mklement0 Sep 26 '19 at 20:22
-3

We can use try catch finally method instead of using && method in powershell.

try {hostname} catch {echo err} finally {ipconfig /all | findstr bios}
Arount
  • 9,853
  • 1
  • 30
  • 43
Cyberiron
  • 9
  • 1
  • 1
    `try` / `catch` is pointless here, because it is only needed for _terminating_ errors, which an external utility such as `hostname` cannot trigger. Your code will _unconditionally_ execute _both_ commands. – mklement0 Jan 21 '17 at 19:18