Skip to content

-[Operator]:$false not respected in Where-Object #26480

Closed

Description

@yotsuda

Prerequisites

Steps to reproduce

This is a sub-issue of #25242.

The cmdlet Where-Object does not respect explicit $false values for any of its 31 operator switch parameters. This affects all comparison and logical operator switches.

Example with -EQ:

$data = @(
    [PSCustomObject]@{ Name = 'Alice'; Age = 25 }
    [PSCustomObject]@{ Name = 'Bob'; Age = 30 }
)
$result = $data | Where-Object Name -EQ:$false
$result.Count

Example with -GT:

$result = $data | Where-Object Age -GT:$false
$result.Count

Expected behavior

PS> $result = $data | Where-Object Name -EQ:$false
PS> $result.Count
2

When -EQ:$false is specified, the -EQ operator should not be active. The cmdlet should fall back to default boolean evaluation of the property value, returning all objects where Name is truthy (non-empty strings).

Similarly for other operators, -[Operator]:$false should behave as if the operator switch was not specified at all.

Actual behavior

PS> $result = $data | Where-Object Name -EQ:$false
PS> $result.Count
0

The -EQ:$false is incorrectly interpreted as -EQ $false, comparing the Name property against the boolean value $false instead of disabling the -EQ operator. This causes the filter to return no results because no Name values equal $false.

Affected parameters

All 31 operator switch parameters are affected:

Category Parameters
Equality -EQ, -CEQ, -NE, -CNE
Comparison -GT, -CGT, -GE, -CGE, -LT, -CLT, -LE, -CLE
Pattern -Like, -CLike, -NotLike, -CNotLike, -Match, -CMatch, -NotMatch, -CNotMatch
Containment -Contains, -CContains, -NotContains, -CNotContains, -In, -CIn, -NotIn, -CNotIn
Type -Is, -IsNot
Logical -Not

Error details

No error is thrown. The cmdlet executes but misinterprets the explicit $false value as an operand rather than as a directive to disable the switch.

Environment data

PS> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.5.4
PSEdition                      Core
GitCommitId                    7.5.4
OS                             Microsoft Windows 10.0.26100
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Activity

  1. TobiasPSP commented on Nov 19, 2025

    @TobiasPSP
    Collaborator

    Parameters in the simplified syntax in Where-Object are supposed to mimick operator behavior.

    While this seems to fall into the typical bucket where parameters do not support :, in this specific case I doubt the real relevance.

    What would be a practical use case where the current behavior exhibits a problem?

  2. mklement0 commented on Nov 19, 2025

    @mklement0
    Contributor

    Dr. Tobias Weltner (@TobiasPSP), please see the discussion in the associated PR, starting at #26485 (comment)

  3. TobiasPSP commented on Nov 20, 2025

    @TobiasPSP
    Collaborator

    Thank you.

    The simplified syntax of Where-Object was intended to welcome beginners and shield them from the complexity of scriptblocks and $_.

    For this significant benefit, it was accepted to pay the price: language inconsistency caused by „abusing“ parameters to mimick operators, leading to unintuitive coloring, unexpected casing, inconsistent tool tips, etc.

    Even then, though, parameters like „-gt“ behaved consistently (like true operators), keeping the language predictable and intact.

    If the ask is now to leverage the underlying (accidental) parameter nature, and enable negation via switch parameter syntax (which is not supported by true operators), this over-stretches the original idea and completely turns language consistency upside down. In return, the achieved benefit seems marginal, not justifying this impact (from a language design perspective).

    The consistent approach would be to use the original scriptblock syntax for anything that is beyond a simple comparison.

    Just my personal 2ct, not discussed yet in the WG.

  4. mklement0 commented on Nov 20, 2025

    @mklement0
    Contributor

    I agree that there's no strong need to tackle this and, as argued in the linked comment, I think it is unlikely that user will even try something like -GT:$false, as it is currently the same as -GT and even with the proposed fix it wouldn't offer anything useful.

    What it comes down to is this:

    What is the better behavior IF someone (misguidedly) actually tries -GT:$false?

    • (a) Currently, the :$false part is effectively ignored.

    • (b) With the proposed fix the behavior is as if -GT hadn't been specified at all.

    While we can only speculate as to what a hypothetical user trying -GT:$false meant to happen, with (a) their use of :$false is quietly ignored, which can cause confusion; with (b), assuming that a -Value argument was also passed, they at least get an error (though the current wording requires that the fact that -GT:$false was in effect ignored has to be inferred).

    As a bonus, (b) is also consistent with how other cmdlets are being fixed with respect to negated switches (see #25242).

    So it comes down to whether these - small - benefits are deemed worth having / worth the effort (though quite a bit of effort has already been expended).

  5. iSazonov commented on Nov 21, 2025

    @iSazonov
    Collaborator

    Just my personal 2ct, not discussed yet in the WG.

    Dr. Tobias Weltner (@TobiasPSP) Should we wait WG re-approving or can continue?

  6. added
    CL-BreakingChangeIndicates that a PR should be marked as a breaking change in the Change Log
    CL-GeneralIndicates that a PR should be marked as a general cmdlet change in the Change Log
    on Nov 21, 2025
  7. iSazonov commented on Nov 21, 2025

    @iSazonov
    Collaborator

    Since we change behavior I set these labels. Breaking change is under Bracket 3 (grey area).

  8. TobiasPSP commented on Nov 21, 2025

    @TobiasPSP
    Collaborator

    If it was approved then all is fine :-)

  9. removed
    CL-BreakingChangeIndicates that a PR should be marked as a breaking change in the Change Log
    CL-GeneralIndicates that a PR should be marked as a general cmdlet change in the Change Log
    on Nov 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions