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.
CodePudding user response:
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 submitExport-Archive
as a command; to find the effective one, useGet-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 excludesExternalScript
(*.ps1
script files) andApplication
(external programs).- Note:
-Type
accepts multiple values, so to find all external scripts and programs, for instance, pass-Type ExternalScript, Application
- Note:
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 asGet-Command -Type All
[1] Those discoverable via the directories listed in $env:PSModulePath
, i.e. via module auto-loading