powershellenumerationparentheses

Why do we need brackets in this example?


Why this

Get-WinUserLanguageList | Where-Object LanguageTag -eq en-US

returns empty list (seems it does not filter values) but this

(Get-WinUserLanguageList) | Where-Object LanguageTag -eq en-US

does work? Usually I don't need () but in this case they are mandatory, but why?


Solution

  • Usually I don't need () but in this case they are mandatory, but why?

    • (...) forces enumeration of the elements of a collection output by the enclosed command in a pipeline.

    • This shouldn't be necessary, but is in your case, because Get-WinUserLanguageList exhibits nonstandard behavior: instead of outputting multiple result objects one by one to the pipeline, it emits an entire collection[1] as a single output object.

      • Without the enclosing (...), the command in the next pipeline segment - Where-Object in your case - therefore receives just one input - the entire collection - and operates on it rather than on the elements one by one.
        Since the collection object itself has no LanguageType property, nothing matches, and you get no output.[2]

    As mentioned in the comments, you can pipe a command's output to Get-Member to see the (distinct) types of its output objects; for standard cmdlets, you'd see the types of the individual objects output, not a collection type.


    [1] Specifically, the collection is a generic list of type [System.Collections.Generic.List[Microsoft.InternationalSettings.Commands.WinUserLanguage]].

    [2] You're using simplified syntax in your command - Where-Object LanguageType -eq en-US - instead of the more verbose, but more flexible script-block syntax - Where-Object { $_.LanguageType -eq 'en-US' }. Had you used the latter, your command would have accidentally returned the entire collection and thereby effectively all languages. The reason is that only the script-block syntax applies member-access enumeration to the input collection, which means that even though $_ itself doesn't have a .LanguageTag property, the elements do, and their values are returned as an array. With an array as the LHS, -eq acts as a filter, and as long as en-US is among the values returned, the -eq operation will still be considered $true, causing the input object - the entire collection - to be passed through.
    This surprising discrepancy in behavior between the two seemingly equivalent syntax form is discussed in GitHub issue #9576.