Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I list all overloaded cmdlets defined in PowerShell?

Tags:

powershell

Context: One of our teams has a Chocolatey package that calls Expand-Archive. However, I have the pscx module installed which also provides an Expand-Archive cmdlet whose parameter signature differs from the built-in cmdlet, and causes this package installation to fail. The solution is to use the module-qualified name in case of such an overload. In the end was able to look at the online documentation to get the module name for what it's worth.

Initially, I tried getting the module with:

Get-Command Expand-Archive -All

but this only listed the cmdlet found in the pscx module. It seems that Get-Command -All does not return commands from available modules which have not been loaded yet. This somewhat makes sense for modules which can't be autoloaded but not the built-in ones like Microsoft.PowerShell.Archive, and this is concerning as it leaves me unable to audit the available commands in my session without first loading the module.

Once I load the module with one of the following methods:

Microsoft.PowerShell.Archive\Expand-Archive
Import-Module Microsoft.PowerShell.Archive

Get-Command Expand-Archive -All indeed shows both definitions in the resolved order, and the module they belong to. This is problematic though, because it inhibits my abililty to audit the commands available on my system, short of blindly importing all available modules before running Get-Command (which is a problematic solution in its own right).


The ask: How can I either tell Get-Command to actually retrieve all resolvable commands available to the session, or obtain this information via other means? As modules are auto-imported based on the first usage of one of its functions (something knows the command is in an unloaded module to do the import), I would expect Get-Command should be able to support this.

like image 350
Bender the Greatest Avatar asked Sep 16 '21 17:09

Bender the Greatest


1 Answers

To find all Export-Archive commands from among the available modules[1] (as opposed to those currently loaded (imported)):

Get-Module -ListAvailable |
  ForEach-Object { 
    if ($cmd = $_.ExportedCommands['Expand-Archive']) { $cmd }
  }

Note: The [System.Management.Automation.PSModuleInfo] instances output by Get-Module also have command-type-specific .ExportedAliases, .ExportedFunctions, and .ExportedCmdlets properties, as well as .ExportedVariables.

Note:

  • If you have multiple versions of a module containing such a command installed, the command from each version will be listed separately.

  • The output won't tell you which Export-Archive command is the effective one, i.e. which one will actually be executed if you submit Export-Archive as a command; to find the effective one, use Get-Command Export-Archive.


As for what you tried:

Get-Command includes commands from available modules by default in principle, but by default only shows the effective command by that name.

The -All switch is meant to show all available commands by a given name, even those being shadowed by the effective command.

As you've observed, the use of -All unexpectedly limits the candidate pool of module-originating commands to those from modules currently loaded, up to at least PowerShell Core 7.2.0-preview.9.

Arguably, this is a bug, and has been reported in GitHub issue #16116


Optional Get-Command information:

Get-Command's behavior without a name (-Name) argument - whether with or without -All - is a bit obscure (albeit mostly documented):

  • Unless you pass a -Type(-CommandType) argument to explicitly control what command types to report, only the following command types are reported: Alias, Function, Filter, Cmdlet, which notably excludes ExternalScript (*.ps1 script files) and Application (external programs).

    • Note: -Type accepts multiple values, so to find all external scripts and programs, for instance, pass -Type ExternalScript, Application
  • To report all command types, use -Type All

  • Curiously, not specifying a -Name argument seems to apply the -All switch, i.e. shadowed commands are invariably included.

By contrast, if a -Name argument is specified, all command types are considered, and whether or not -All is specified does make a difference.

  • Thus, Get-Command * is effectively the same as Get-Command -Type All

[1] Those discoverable via the directories listed in $env:PSModulePath, i.e. via module auto-loading

like image 72
mklement0 Avatar answered Oct 13 '22 16:10

mklement0