windowsazurepowershellgrepselect-string

Select-String Doesn't Show All Matches With Get-AppxPackage


I get all the packages installed on my PC using Get-AppxPackage and I'm trying to find all the matches in that with N lines before and after using Select-String.

However, the select string is only showing a matches as single line and it's not showing all the matches either. This only happens when I pipe the output from Get-AppxPackage and not if I write it to a file and then do cat <filename> | select-string ....

As you can see in the example below the two results of using pipe and cat. I'm interested in results like from cat i.e. detailed info about the app.

So what am I doing wrong here? Why is the output different?


Example (everyone should have MS Edge so I'll use that as an example) :

PS > Get-AppxPackage | Select-String -pattern 'edge' -context 3, 3 -allmatches

  Microsoft.Windows.StartMenuExperienceHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Microsoft.Windows.Cortana_1.13.0.18362_neutral_neutral_cw5n1h2txyewy
  Microsoft.AAD.BrokerPlugin_1000.18362.329.0_neutral_neutral_cw5n1h2txyewy
> Microsoft.MicrosoftEdge_44.18362.329.0_neutral__8wekyb3d8bbwe
  Microsoft.Windows.CloudExperienceHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Microsoft.Windows.ContentDeliveryManager_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Windows.CBSPreview_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Microsoft.Windows.Apprep.ChxApp_1000.18362.329.0_neutral_neutral_cw5n1h2txyewy
  Microsoft.Win32WebViewHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Microsoft.PPIProjection_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
> Microsoft.MicrosoftEdgeDevToolsClient_1000.18362.329.0_neutral_neutral_8wekyb3d8bbwe
  Microsoft.LockApp_10.0.18362.329_neutral__cw5n1h2txyewy
> Microsoft.EdgeDevtoolsPlugin_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
  Microsoft.ECApp_10.0.18362.329_neutral__8wekyb3d8bbwe
  Microsoft.CredDialogHost_10.0.18362.329_neutral__cw5n1h2txyewy
  Microsoft.BioEnrollment_10.0.18362.329_neutral__cw5n1h2txyewy

PS > cat .\appx-packages.txt | select-string -pattern 'edge' -context 3, 3 -allmatches

  SignatureKind     : System
  Status            : Ok

> Name              : Microsoft.MicrosoftEdge
  Publisher         : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
  Architecture      : Neutral
  ResourceId        :
  Version           : 44.18362.329.0
> PackageFullName   : Microsoft.MicrosoftEdge_44.18362.329.0_neutral__8wekyb3d8bbwe
> InstallLocation   : C:\Windows\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe
  IsFramework       : False
> PackageFamilyName : Microsoft.MicrosoftEdge_8wekyb3d8bbwe
  PublisherId       : 8wekyb3d8bbwe
  IsResourcePackage : False
  IsBundle          : False
  SignatureKind     : System
  Status            : Ok

> Name              : Microsoft.MicrosoftEdgeDevToolsClient
  Publisher         : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
  Architecture      : Neutral
  ResourceId        : neutral
  Version           : 1000.18362.329.0
> PackageFullName   : Microsoft.MicrosoftEdgeDevToolsClient_1000.18362.329.0_neutral_neutral_8wekyb3d8bbwe
> InstallLocation   : C:\Windows\SystemApps\Microsoft.MicrosoftEdgeDevToolsClient_8wekyb3d8bbwe
  IsFramework       : False
> PackageFamilyName : Microsoft.MicrosoftEdgeDevToolsClient_8wekyb3d8bbwe
  PublisherId       : 8wekyb3d8bbwe
  IsResourcePackage : False
  IsBundle          : False
  SignatureKind     : System
  Status            : Ok

> Name              : Microsoft.EdgeDevtoolsPlugin
  Publisher         : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
  Architecture      : Neutral
  ResourceId        : neutral
  Version           : 10.0.18362.329
> PackageFullName   : Microsoft.EdgeDevtoolsPlugin_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
> InstallLocation   : C:\Windows\SystemApps\Microsoft.EdgeDevtoolsPlugin_cw5n1h2txyewy
  IsFramework       : False
> PackageFamilyName : Microsoft.EdgeDevtoolsPlugin_cw5n1h2txyewy
  PublisherId       : cw5n1h2txyewy
  IsResourcePackage : False
  IsBundle          : False



Solution

  • tl;dr

    As of PowerShell v7.3, to make Select-String search non-string input by the same rich string representation you'd see in the console (terminal), you must pipe the input to oss first:

    Get-AppxPackage | oss | Select-String -Pattern 'edge' -Context 3, 3
    

    Note:


    Background information:

    Select-String, when given input other than strings, uses simple .ToString() stringification[2] on each input object before looking for the given pattern.

    In your case, the [Microsoft.Windows.Appx.PackageManager.Commands.AppxPackage] instances output by Get-AppXPackage stringify to the full package names (e.g., Microsoft.MicrosoftEdge_44.18362.387.0_neutral__8wekyb3d8bbwe), which explains your output.

    In order to make Select-String search the for-display string representations of objects - as they would print to the console and as they would appear in a file saved to with > / Out-File (cat is Out-File's built-in alias on Windows) - you must, surprisingly,
    use Out-String -Stream as an intermediate pipeline segment; since PowerShell v5, if memory serves, you can use built-in wrapper function oss for brevity:

    # oss is a built-in wrapper function for Out-String -Stream
    Get-AppxPackage | oss | Select-String -Pattern 'edge' -Context 3, 3
    

    Out-String uses PowerShell's formatting system to produce human-friendly display representations of the input objects, the same way that default console output, the Format-* cmdlets, and > / Out-File do.
    -Stream causes the output lines to be sent through the pipeline one by one, so that Select-String can match individual lines.


    Given that the solution is both non-obvious and cumbersome, it would be nice if Select-String directly supported this behavior, ideally by default, but at least on an opt-in basis via a switch parameter - see feature request #10726 on GitHub - up-vote the proposal there if you agree.


    [1] As of v7.3, PowerShell only "speaks text" when communicating with external programs, so it has to create a string representation of non-string objects when passing data to them: see this answer. While it makes sense to default to the for-display string representation of such objects, note that this representation isn't meant for programmatic processing; for the latter, it's best to explicitly output a structured text format, such as JSON via ConvertTo-Json.

    [2] More accurately, .psobject.ToString() is called, either as-is, or - if the object's ToString method supports an IFormatProvider-typed argument - as .psobject.ToString([cultureinfo]::InvariantCulture) so as to obtain a culture-invariant representation - see this answer for more information.