Using Prefixes to Prevent Command Name Collision
- Details
- Written by June Blender
- Last Updated: 19 April 2016
- Created: 15 February 2016
- Hits: 10268
In January, I had the honor of presenting to the Mississippi PowerShell User Group (MSPSUG). I’ve known the organizers, Mike Robbins and Rohn Edwards for years, and truly respect them. The PSUG is online-only, which makes it a challenge for presenters, but they attract a very sophisticated audience, so my talks there are really conversations. This was a perfect venue for my “Avoiding Version Chaos” talk. (More at PowerShell Saturday in Tampa on March 19, 2016.)
In one part of the talk, I demonstrated how to use noun prefixes to distinguish among commands with the same names. The demo flopped — we ended up with duplicate commands — so I’ll use this blog post to show how prefixes work and what went wrong.
TIP: To detect commands with the same name on the same machine, Using Group-Object to Detect Command Name Conflicts.
Define Unique (Prefixed) Names
One way to prevent command name conflicts is to define command names that are likely to be unique.
For example, the cmdlets in the PowerForensics module are created with names that include “Forensic” so they’re likely to be unique. (Note that some commands in the module, like ConvertFrom-BinaryData, are not prefixed, because they are intended for a more general use.)
PS C:\> Get-Command -Module PowerForensics ommandType Name Version Source ----------- ---- ------ ------ Cmdlet ConvertFrom-BinaryData 1.1.1 PowerForensics Cmdlet ConvertTo-ForensicTimeline 1.1.1 PowerForensics Cmdlet Copy-ForensicFile 1.1.1 PowerForensics Cmdlet Get-ForensicAlternateDataStream 1.1.1 PowerForensics Cmdlet Get-ForensicAmcache 1.1.1 PowerForensics Cmdlet Get-ForensicAttrDef 1.1.1 PowerForensics Cmdlet Get-ForensicBitmap 1.1.1 PowerForensics Cmdlet Get-ForensicBootSector 1.1.1 PowerForensics ...
Add a Default Command Prefix
To prevent name conflicts, module authors can also create commands with more generic names and then specify a default command prefix in the module manifest (the .psd1 file of the module). Then, when the module is imported, Import-Module cmdlet prepends the default prefix to the nouns of all commands in the module.
To specify a default prefix, use the DefaultCommandPrefix key in the module manifest.
DefaultCommandPrefix =
|
To get modules with a default command prefix, look for a value in the Prefix property of the module.
PS C:\> Get-Module -ListAvailable | where Prefix Directory: C:\Users\JuneBlender\Documents\WindowsPowerShell\Modules ModuleType Version Name ExportedCommands ---------- ------- ---- ---------------- Manifest 1.2.0.0 HardwareManagement {Get-CIMHardwareInventory, Get-CIMBootOrder,
For example, the HardwareManagement module has several functions with names that might appear in other modules, such as Get-Account and Get-Computer System. So, the module author defined a default prefix, CIM. Let’s look at it.
This command gets the path to module manifest and then converts the manifest to hash table, so it’s easier to examine.
(h/t @LeeHolmes for the command format. It converts any hash table string a hash table).
#Convert the module manifest to a hash table # The manifest path is in the module's Path property PS C:\> $manifest = Invoke-Expression (Get-Content -Raw -Path ((Get-Module -List HardwareManagement).Path)) |
Here’s the DefaultCommandPrefix key. It has a value of ‘CIM’.
PS C:\> $manifest.DefaultCommandPrefix CIM
The manifest also reveals that the functions in the HardwareManagement module, as defined, don’t have the ‘CIM’ prefix in the name.
PS C:\> $manifest.FunctionsToExport Clear-RecordLog ConvertTo-OctetString Disable-Account Enable-Account Get-Account Get-AccountMgmtService Get-BootOrder Get-ComputerSystem Get-ConsoleRedirection ...
However, when you import the modules into the session, the nouns in the function names have the ‘CIM’ prefix.
PS C:\> Import-Module HardwareManagement PS C:\> Get-Command -Module HardwareManagement CommandType Name Version Source ----------- ---- ------- ------ Function Clear-CIMRecordLog 1.2.0.0 HardwareManagement Function ConvertTo-CIMOctetString 1.2.0.0 HardwareManagement Function Disable-CIMAccount 1.2.0.0 HardwareManagement Function Enable-CIMAccount 1.2.0.0 HardwareManagement Function Get-CIMAccount 1.2.0.0 HardwareManagement Function Get-CIMAccountMgmtService 1.2.0.0 HardwareManagement Function Get-CIMBootOrder 1.2.0.0 HardwareManagement ...
Get-Help recognizes the command name with its prefix. Note that Get-Help automatically includes the prefix in the Name, Syntax, and Examples, but not in descriptions and other written text.
PS C:\> Get-Help Clear-CIMRecordLog -full NAME Clear-CIMRecordLog SYNOPSIS Clears a record log SYNTAX Clear-CIMRecordLog -CimSession -InstanceID [-UseRecordLogProfile] [-WhatIf] [-Confirm] [] Clear-CIMRecordLog [-CimRecordLog] [-UseRecordLogProfile] [-WhatIf] [-Confirm] [] DESCRIPTION Removes all entries from a specific record log from managed node based on support of Record Log Profile More details about the Record Log Profile can be found here: http://www.dmtf.org/sites/default/files/standards/documents/DSP1010_1.0.pdf http://www.dmtf.org/sites/default/files/standards/documents/DSP1010_2.0.pdf PARAMETERS -CimRecordLog ...
And, you can run the command as usual with the prefix.
PS C:\> Clear-CimRecord -CimSession $cs InstanceID 1
Specify a custom prefix
The DefaultCommandPrefix is just a default. You can specify a custom prefix for the commands any module. If the module has a default command prefix, it is ignored and the custom prefix that you specify is used instead.
To specify a custom prefix for the commands in a module, use the Prefix parameter of Import-Module.
For example, because I have both the Microsoft.PowerShell.Archive and PSCX module on my test machine, I have two commands named Expand-Archive. (Note the wildcard in the command.)
#Note the wildcard. Otherwise, it would return only the function.
PS C:\> Get-Command Expand-Archive*
CommandType Name Version Source
----------- ---- ------- ------
Function Expand-Archive 1.0.0.0 Microsoft.PowerShell.Archive
Cmdlet Expand-Archive 3.2.1.0 Pscx
By default, Windows PowerShell runs the Expand-Archive function, because functions take precedence over cmdlets. So, to make it easier to run the PSCX cmdlet, I specify a ‘PSCX’ prefix when I import the PSCX module.
PS C:\> Import-Module PSCX -Prefix PSCX PS C:\> Get-Command Expand-*Archive CommandType Name Version Source ----------- ---- ------- ------ Function Expand-Archive 1.0.0.0 Microsoft.PowerShell.Archive Cmdlet Expand-PSCXArchive 3.2.1.0 PSCX
Now, it’s easy to distinguish the commands and use the one I want.
PS C:\ > Expand-PSCXArchive -OutputPath ... PS C:\ > Expand-Archive -DestinationPath ...
If a module has a DefaultCommandPrefix, the prefix that you specify in your Import-Module command is used instead of the default. For example, the default command prefix for the HardwareManagement module is ‘CIM’, but I prefer ‘Hardware’.
By default, the command prefix is CIM.
PS C:\> Import-Module HardwareManagement PS C:\> Get-Command -Module HardwareManagement | Sort Name CommandType Name Version Source ----------- ---- ------- ------ Function Clear-CIMRecordLog 1.2.0.0 HardwareManagement Function ConvertTo-CIMOctetString 1.2.0.0 HardwareManagement Function Disable-CIMAccount 1.2.0.0 HardwareManagement Function Enable-CIMAccount 1.2.0.0 HardwareManagement Function Get-CIMAccount 1.2.0.0 HardwareManagement Function Get-CIMAccountMgmtService 1.2.0.0 HardwareManagement Function Get-CIMBootOrder 1.2.0.0 HardwareManagement ...
Specify the ‘Hardware’ value of the Prefix parameter.
PS C:\> Remove-Module HardwareManagement PS C:\ps-test> Import-Module HardwareManagement -Prefix Hardware PS C:\ps-test> Get-Command -Module HardwareManagement | Sort Name CommandType Name Version Source ----------- ---- ------- ------ Function Clear-HardwareRecordLog 1.2.0.0 HardwareManagement Function ConvertTo-HardwareOctetString 1.2.0.0 HardwareManagement Function Disable-HardwareAccount 1.2.0.0 HardwareManagement Function Enable-HardwareAccount 1.2.0.0 HardwareManagement Function Get-HardwareAccount 1.2.0.0 HardwareManagement Function Get-HardwareAccountMgmtService 1.2.0.0 HardwareManagement Function Get-HardwareBootOrder 1.2.0.0 HardwareManagement ...
Limits of Command Prefixes
Prefixes are a great solution for avoiding name conflicts, right? Well, sometimes. But a lot of things can go wrong. One of them went wrong in my demo, but that’s actually a good reminder.
As pointed out by one of the Mississippi PowerShell User Group participants (one of many really great conversations), it’s not a good idea to use prefixes in a script or module that you share with others. You cannot predict what else is in the session and you might actually create a name conflict, rather than resolving one.
Also, because modules are imported automatically, it’s easy to end up with multiple copies of the same command in the session. That’s what happened in my demo (but, unfortunately, not in my practice sessions).
First I showed that the commands in the module had no intrinsic noun prefix.
PS C:\> (Invoke-Expression (Get-Content -Raw (Get-Module HardwareManagement -List).Path )).FunctionsToExport | Sort Clear-RecordLog ConvertTo-OctetString Disable-Account Enable-Account Get-Account Get-AccountMgmtService Get-BootOrder
Next, I showed that PowerShell automatically used the specified DefaultCommandPrefix value of CIM.
PS C:\> Get-Command -Module HardwareManagement CommandType Name Version Source ----------- ---- ------- ------ Function Clear-HardwareRecordLog 1.2.0.0 HardwareManagement Function ConvertTo-HardwareOctetString 1.2.0.0 HardwareManagement Function Disable-HardwareAccount 1.2.0.0 HardwareManagement Function Enable-HardwareAccount 1.2.0.0 HardwareManagement Function Get-HardwareAccount 1.2.0.0 HardwareManagement Function Get-HardwareAccountMgmtService 1.2.0.0 HardwareManagement Function Get-HardwareBootOrder 1.2.0.0 HardwareManagement ...
Then, I showed how to use the Prefix parameter of the Import-Module cmdlet to define your own prefix.
PS C:\> Import-Module HardwareManagement -Prefix Hardware
But, when I displayed the commands in my session, I had both commands with a CIM prefix and commands with a Hardware prefix.
PS C:\> Get-Command -Module HardwareManagement CommandType Name Version Source ----------- ---- ------- ------ Function Clear-CIMRecordLog 1.2.0.0 HardwareManagement Function Clear-HardwareRecordLog 1.2.0.0 HardwareManagement Function ConvertTo-CIMOctetString 1.2.0.0 HardwareManagement Function ConvertTo-HardwareOctetString 1.2.0.0 HardwareManagement Function Disable-CIMAccount 1.2.0.0 HardwareManagement Function Disable-HardwareAccount 1.2.0.0 HardwareManagement Function Enable-CIMAccount 1.2.0.0 HardwareManagement Function Enable-HardwareAccount 1.2.0.0 HardwareManagement Function Get-CIMAccount 1.2.0.0 HardwareManagement ...
I thought PowerShell might be at fault, but the fault was mine. The Get-Command command auto-loaded the module with the CIM-prefixed commands. Then, I explicitly imported the Hardware-prefixed commands. This isn’t a practical problem, because running the commands with either name would work, but it’s certainly confusing.
I’ll be talking and blogging about module and command conflicts over the next few months. If you have questions or suggestions, please let me know. And, thanks to the Mississippi PowerShell User Group for the great participation.
June Blender is a technology evangelist at SAPIEN Technologies, Inc. You can reach her at This email address is being protected from spambots. You need JavaScript enabled to view it. or follow her on Twitter at @juneb_get_help.
For licensed customers, use the forum associated with your product in our Product Support Forums for Registered Customers.
For users of trial versions, please post in our Former and Future Customers - Questions forum.