Skip to content

Suggestion: implement Bash-style && and || control operators for chaining commands based on success / failure - short-circuiting Boolean execution-flow control operators #3241

Closed

Description

Bash-style && and || control operators would be handy additions to the language.

For instance, instead of writing (the examples are contrived, but hopefully illustrate the point):

# If command A succeeds, execute command B too.
csc /nologo file.cs
if (0 -eq $LASTEXITCODE) { git commit -m 'ok' }

# if command A fails, execute command B.
csc /nologo file.cs
if (0 -ne $LASTEXITCODE) { git commit -m 'failed'  }

one might be able to write:

# If command A succeeds, execute command B too.
csc /nologo file.cs && git commit -m 'ok'

# if command A fails, execute command B.
csc /nologo file.cs || git commit -m 'failed'

The examples use external utilities, but these operators would be equally handy for PowerShell-native commands (where $? rather than $LASTEXITCODE would have to be examined).


&& and || were on the list of things to implement (still are) but did not pop up as the next most useful thing to add.

There are several other questions on Stack Overflow asking for this feature and I've tried to summarize the state of affairs in this answer.

Activity

  1. added this to the milestone on Mar 4, 2017
  2. changed the title [-]Suggestion: implement Bash-style && and || control operators[/-] [+]Suggestion: implement Bash-style && and || control operators for chaining commands based on success / failure[/+] on Aug 14, 2017
  3. i-give-up commented on Oct 3, 2017

    @i-give-up

    I believe the proper term for && and || is "short-circuit operator".

  4. changed the title [-]Suggestion: implement Bash-style && and || control operators for chaining commands based on success / failure[/-] [+]Suggestion: implement Bash-style && and || control operators for chaining commands based on success / failure - short-circuiting Boolean execution-flow control operators[/+] on Oct 3, 2017
  5. mklement0 commented on Oct 3, 2017

    @mklement0
    ContributorAuthor

    i-give-up: While short-circuiting is an important aspect of these operators' behavior, both Bash and POSIX call them control operators.

    If you wanted to get as descriptive as possible, I guess you could call them short-circuiting Boolean execution-flow control operators.

    So as to make this issue more easily discoverable, I've appended that term to the issue's title.

  6. lenkite commented on Oct 18, 2017

    @lenkite

    Short circuit operators are such a fundamental part of the daily shell use at the command line and it is a massive burden to work without them. And the -and/-or operators are not a replacement since they simply output the boolean result of the operation making them useless for interactive use.

  7. stofte commented on Nov 9, 2017

    @stofte

    I dont understand what the -and operator actually does (and presumably the -or as well).

    It's simply baffling to have the following code and see "True" come out when you run it

    function Foo { return $True }
    function Bar { return Foo -and $False }
    Write-Host "Bar: $(Bar)"
    

    The OPs stackoverflow really shows everything that is fubared about this design.

  8. mklement0 commented on Nov 9, 2017

    @mklement0
    ContributorAuthor

    Svend Tofte (@stofte): PowerShell has 2 personalities (fundamental syntax forms): argument mode (like a shell) and expression mode (like a programming language) - see Get-Help about_Parsing.

    -and and -or belong to the expression-mode world and operate on (often inferred) Boolean values.
    They have no equivalent in argument mode, and that's the crux of the problem.

    Your example, Foo -and $False is parsed in argument mode, because the statement's first token is a command. Therefore, you're passing tokens -and and -$False as arguments (parameter values) to function Foo - where, in your case, they're ignored and the output is simply $True.

    If you want to use a command (argument mode) as part of an expression (expression mode; e.g., with -and), you must enclose it in parentheses:

    return (Foo) -and $False
    
  9. stofte commented on Nov 10, 2017

    @stofte

    Michael Klement (@mklement0) Thanks for the explanation. It's still not entirely clear how argument modes relates and interacts with outcome vs output parsing/interpretation however.

    PS is probably the most confusing language I've come across in my years. It seems to assemble a hodge-podge of ideas in order to be everything for everyone (anyway, ranting over!)

  10. dragonwolf83 commented on Nov 10, 2017

    @dragonwolf83

    Svend Tofte (@stofte) You pass parameters to functions via the - character so any - characters after the function are going to be assumed to be parameters. So Foo -and $false is really trying to pass -and as the parameter name with $false as the value but since Foo doesn't define any parameters, it just ignores it.

    Let's change your script a bit to add parameters and see how it works.

    function Foo 
    { 
        Param($and) 
        Write-Host "And = $and"
        
        return $True 
    
    }
    function Bar { Foo -and $False }
    Write-Host "Bar: $(Bar)"

    Now, Foo will accept the parameter and pass $false into the function. However, it still returns $true since that is what the function outputs.

    So for PowerShell to know if it shouldn't be trying to pass -and as a parameter, you need to use parenthesis to control order of operations. (Foo) -and $false now will run Foo first, then evaluate the remaining statement $true -and -$false.

    BTW, PowerShell ISE and Visual Studio Code is smart enough to tell you all of this via Intellisense. It knows that the -and operator can't be used in your original code and won't give it as an option. I highly recommend using Intellisense to make sure keywords are usable in the right context.

    Now for why functions are using - to pass parameters is because the idea behind PowerShell is to create command-line applications through functions instead of having to compile an application. So a Function mimics much of the command-line experience to make it happen.

  11. jurriaanr commented on Apr 13, 2018

    @jurriaanr

    I can't believe I still have to start up cmd for this functionality, my bash using colleagues are making fun of me

  12. rjmholt commented on Apr 21, 2018

    @rjmholt
    Collaborator

    I've been thinking about this a bit.

    A thought I have is that if && and || only look at $LASTEXITCODE it makes them pretty specific -- two new operators that only work in the context of native commands. And without good knowledge of that fact, something like

    csc.exe program.cs
    
    Foo && Bar

    could prove very confusing, since it either uses the $LASTEXITCODE value, in which case Bar being executed depends on the success of the csc.exe line. OR, if just assumes that a non-native command succeeds (or whatever) that is just as confusing. OR, we throw an error because Foo isn't a native command...

    So I was thinking about something more like JavaScript's (please, hear me out...) short circuit operator implementation. That way you could do:

    > "" && "Hello"
    
    > "Hi" && "Hello"
    Hello
    
    >"Hi" || "Hello"
    Hi
    
    > "" || "Hello"
    Hello

    Specifically, Foo && Bar evaluates Foo and if the result coerces to $false, it returns that (pre-coercion) value, otherwise it returns the result of Bar. With Foo || Bar, Foo is evaluated and if the result coerces to $true it returns that (pre-coercion) result, otherwise we evaluate and return the result of Bar. The operators would have equal precedence and be left-associative, so you can chain them like so:

    > "" || "Hello" && "Goodbye"
    Goodbye

    That last thing is pretty confusing, but would be useful for chains of only one operator (Step1 && Step2 && Step3, Try1 || Failover1 || Failover2) and I see no particular reason not to support a mixed chain.

    Notice that this also gives us a null-coalescing functionality like so: $x = $v || "default". With a mixed chain you'd even get a (rather confusing) ternary operator: $condition && $ifTrue || $ifFalse.

    However, the problem is that this wouldn't scratch the itch of wanting a way to chain native commands. So, we could make it that when one of these operators evaluates a native command as an argument, it proceeds based on the $LASTEXITCODE value, so that the following work:

    > csc.exe badprogram.cs && RunProgram
    # error output from csc.exe, RunProgram not evaluated
    
    > SetupProject && csc.exe goodprogram.cs
    # output from csc.exe, SetupProject evaluated

    I think that's a bit complex, but unfortunately it seems to be most sensible and useful option I can see.

    Anyway, please let me know your thoughts on this -- I'm just spitballing ideas here (although I'm always in favour of consistency). After some initial thoughts/comments here, I'd like to write up an RFC with a more concrete proposal and then implement a prototype.

  13. saschanaz commented on Apr 21, 2018

    @saschanaz

    So I was thinking about something more like JavaScript's (please, hear me out...) short circuit operator implementation

    I still think we need to use $LASTEXITCODE. For example, think about npm run build && npm run deploy. npm run build can emit some text even when it fails, and I want to prevent npm run deploy from running after a failed build.

  14. 22 remaining items

  15. mikemaccana commented on Apr 6, 2019

    @mikemaccana

    Steve Lee (@SteveL-MSFT) Since there won't be a 6.3 - consider it for 7?

  16. SteveL-MSFT commented on Apr 7, 2019

    @SteveL-MSFT
    Member

    Mike MacCana (@mikemaccana) I've renamed the 6.3-Consider milestone to 7.0-Consider

  17. vladimiry commented on Apr 27, 2019

    @vladimiry
  18. mklement0 commented on Apr 27, 2019

    @mklement0
    ContributorAuthor

    Vladimir Yakovlev (@vladimiry) Thanks, but this issue is about a concise, success-conditional idiom for sequencing commands; by contrast, your link is (primarily) about unconditional parallel execution via PowerShell workflows, which aren't supported in PowerShell Core.

  19. rjmholt commented on Jun 12, 2019

    @rjmholt
    Collaborator
  20. Raffaello commented on Jan 15, 2022

    @Raffaello

    in powershell ver 5.1 are still not present, which version of Powershell include these operators?

  21. kilasuit commented on Jan 15, 2022

    @kilasuit
    Collaborator

    Raffaello (@Raffaello) If you checked the PR that closed this you'd have seen it was added in for 7.0.0-preview5 - so it is in Powershell since the GA of v7.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Issue-Enhancementthe issue is more of a feature request than a bugResolution-FixedThe issue is fixed.WG-Languageparser, language semantics

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions