Home > Blockchain >  Get-Module -ListAvailable: Why or how are modules printed in sections divided by Directory?
Get-Module -ListAvailable: Why or how are modules printed in sections divided by Directory?

Time:11-24

When I do "Get-Module -ListAvailable", powershell will print 169 modules. For example:

    Directory: C:\Program Files (x86)\Microsoft SQL Server\150\Tools\PowerShell\Modules


ModuleType Version    Name                                ExportedCommands                                                               
---------- -------    ----                                ----------------                                                               
Manifest   15.0       SQLPS                               {Backup-SqlDatabase, Save-SqlMigrationReport, Invoke-PolicyEvaluation, Resto...


    Directory: C:\Users\user\Documents\WindowsPowerShell\Modules


ModuleType Version    Name                                ExportedCommands                                                               
---------- -------    ----                                ----------------                                                               
Script     3.0.1      DotNetVersionLister                 Get-STDotNetVersion                                                            
Script     1.4.7      PackageManagement                   {Find-Package, Get-Package, Get-PackageProvider, Get-PackageSource...}         
Script     2.2.5      PowerShellGet                       {Find-Command, Find-DSCResource, Find-Module, Find-RoleCapability...}          
Script     2.2.16     VSSetup                             {Get-VSSetupInstance, Select-VSSetupInstance}                                  


    Directory: C:\Program Files\WindowsPowerShell\Modules


ModuleType Version    Name                                ExportedCommands                                                               
---------- -------    ----                                ----------------                                                               
Script     1.3.1      Configuration                       {Import-Configuration, Export-Configuration, Get-StoragePath, Add-MetadataCo...

When I capture this in an array: "$m = Get-Module -ListAvailable" It seems like just a simple array, yet it also prints in these sections.

How is this done?

There doesn't even seem to be a "Directory" property on the PSModuleInfo objects.

CodePudding user response:

Powershell have its own formatting engine. Whenever you use that cmdlet, you output a list of System.Management.Automation.PSModuleInfo objects.

Before printing the object "raw", Powershell check if there is a predefined formatting available for the type and if so, apply it. What you see is the result of that transformation.

Up to PS 5.1, this was done through formatting configuration file, defined as *.ps1xml files. From PS6.0 and newer, predefined formats are now included directly in the source code but you can still create additional format files as needed.

You can view the loaded format type using the Get-FormatData cmdlet.

If you are interested in the Get-Module cmdlet specifically, check out (Get-FormatData -TypeName System.Management.Automation.PSModuleInfo).FormatViewDefinition. You will see something like this:

Name   Control
----   -------
Module System.Management.Automation.TableControl
Module System.Management.Automation.WideControl
Module System.Management.Automation.ListControl

This mean that any objects of that type have special instructions concerning the way that it should output its object. In that case, it includes grouping by path and displaying the specific columns (ModuleType, Version, Name, ExportedCommands). Powershell did not chose to display those properties by itself, it got its instructions from the predefined type on what to display.

In the case of PSModuleInfo type, we can see that there is 3 custom views for the type. One for table view (which is the default shown), one for list and wide, which instruct what to show when you use Format-List & Format-Wide.

From MS doc

The display format for the objects that are returned by commands (cmdlets, functions, and scripts) are defined by using formatting files (format.ps1xml files). Several of these files are provided by PowerShell to define the display format for those objects returned by PowerShell-provided commands, such as the System.Diagnostics.Process object returned by the Get-Process cmdlet. However, you can also create your own custom formatting files to overwrite the default display formats or you can write a custom formatting file to define the display of objects returned by your own commands.

PowerShell uses the data in these formatting files to determine what is displayed and how the displayed data is formatted. The displayed data can include the properties of an object or the value of a script.

You can create your own files (*.ps1xml) and include them in your modules or load them in your sessions to modify the way the output is displayed.

You can also add formatting to the output of your functions by defining a default display set (aka what properties should be displayed).

For instance, take this simple function:


  Function Get-EmployeesInfos() {
    $Output = @(
        
        [PSCustomObject]@{
            FirstName            = 'RObert'
            LastName             = 'Samson'
            SocialSecurityNumber = '123 344 555'
            Age                  = '32'
            Salary               = '100000'
        },
        
        [PSCustomObject]@{
            FirstName            = 'Pablo'
            LastName             = 'Morrison'
            SocialSecurityNumber = '123 345 555'
            Age                  = '22'
            Salary               = '10000'
        }


    )
    
    # Default display set
    $defaultDisplaySet = 'FirstName', 'LastName'
    $defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet)
    $Output | Add-Member MemberSet PSStandardMembers ([System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet)) -Force
    return $Output


    return $Output
  }

Without any default display set, you would get your standard output with all the properties listed.

enter image description here

With the default display set added, here is the new output.

enter image description here

Both outputs do contains the same information, but the console have a special formatting applied to it to show only what is most important, useful, etc...

You can use formatting views to:

  • Colorize output
  • Create trees
  • Change output based on condition
  • Add virtual properties
  • define column width
  • define displayed column title
  • etc...

References:

Formatting File Overview

4Sysops - Formatting object output in Powershell with Format.ps1xml files

Update-FormatData

CodePudding user response:

The reason that Get-Module is showing the result in groups is because that is the default format for Module objects whenever PowerShell shows them to the user. It's not a specific feature of the Get-Module cmdlet as such.

This is convenient facility in general because you can then use cmdlets such as Sort-Object and Where-Object to sort and filter the results and then have the results shown in groups afterwards.

In the following example, the results are filtered and then shown in groups. The significance is that neither Get-Module nor Where-Object is aware that the end result will be shown in groups; they just deal with objects.

PS> Get-Module -ListAvailable | Where-Object Name -Match Read

    Directory: C:\program files\powershell\7\Modules

ModuleType Version    PreRelease Name
---------- -------    ---------- ----
Script     2.1.0                 PSReadLine                          ...
Binary     2.0.3                 ThreadJob                           ...

    Directory: C:\Program Files\WindowsPowerShell\Modules

ModuleType Version    PreRelease Name
---------- -------    ---------- ----
Script     2.0.0      beta2      PSReadline                          ...

You can see what PowerShell is doing in this specific case by looking at the default formatting code for modules on GitHub. The relevant part is the GroupByScriptBlock call (with minor reformatting to reduce line length):

yield return new FormatViewDefinition("Module",
    TableControl.Create()
        .GroupByScriptBlock(@"
            Split-Path -Parent $_.Path | ForEach-Object {
                if([Version]::TryParse((Split-Path $_ -Leaf), [ref]$null)) {
                    Split-Path -Parent $_
                } else {
                    $_
                }
            } | Split-Path -Parent", customControl: sharedControls[0])
        .AddHeader(Alignment.Left, width: 10)

        ...

When PowerShell shows an array of module objects to the user using the default format, it will run the script block in GroupByScriptBlock on each object first to work out the grouping.

  • Related