The Group-Object cmdlet, one of the original provider cmdlets, is as old as Windows PowerShell. It was introduced in version 1.0 and hasn’t changed at all since then. But, it is one of my favorites. (You can tell when I love a cmdlet by the number of examples. Group-Object has 9!) In fact, when you use it frequently, you begin to see groups as a path to many solutions.

Group-Object groups objects by the values of a property that you choose. So, it’s a quick way to find the property values that appear in a data set. Which domain controllers are used by users in this department? What command types are exported by this module? It’s also a great way to detect duplicate and unique values. (Really, try a few of the 9 examples.)

In this post, I’ll be using an old cmdlet to help with a new problem — detecting multiple commands with the same name on the same machine. Because providers and repositories offer more modules, we can install module versions side-by-side on 5.0, and we can install them in a blink without much consideration, name collisions are more likely than ever.

Detect commands with the same name

To detect commands with the same names in your installed modules, use the Get-Command cmdlet to get all commands in all installed modules. Then, use the Group-Object cmdlet to group commands by the value of their Name property. The result is a collection of groups where each group contains commands with the same name.

Some groups have just one member. But, each group that Group-Object returns has a Count property that indicates the number of objects in the group. That makes it easy to use the Where-Object cmdlet to find groups that have more than one member.

$groups = Get-Command | Group-Object -Property Name | Where Count -gt 1 $groups = Get-Command | Group-Object -Property Name | Where Count -gt 1

Let’s look at one of the multi-member groups. This is one for the Describe command in the Pester module. I use and study Pester pretty obsessively, so I have multiple versions of the Pester module on my test machine.

PS C:\> $groups | where Name -eq 'Describe' Count Name Group ----- ---- ----- 6 Describe {Describe, Describe, Describe, Describe...}

This group has 6 members, that is, 6 Describe commands.

Each group that Group-Object returns also has a Group property that contains the members of the group. In this case, the Group property of the Describe group contains the Describe commands.

PS C:\> ($groups | where Name -eq 'Describe').Group CommandType Name Version Source ----------- ---- ------- ------ Function Describe 3.3.13 Pester Function Describe 3.3.11 Pester Function Describe 3.3.9 Pester Function Describe 3.3.10 Pester Function Describe 3.3.8 Pester Function Describe 3.3.5 Pester

The Describe duplicates represent the same command in different versions of the same module. But, we also find commands with the same name in different modules, such as this Set-Clipboard command.

PS C:\> ($groups | where Name -eq 'Set-Clipboard').Group CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Set-Clipboard 3.1.0.0 Microsoft.PowerShell.Management Cmdlet Set-Clipboard 3.2.1.0 Pscx

Detect commands with same name in different modules

To get commands with the same names in modules with different names, use Group-Object to create groups based on the Source (module name) property value of each command and look for groups with more than one unique Source value.

To count the number of sources that the second Group-Object command returns, I use the Length property that PowerShell adds to all objects, because the Count property is the number of objects in the group.

Get-Command | Group-Object -Property Name | where { $_ .Count -GT 1 -and ( $_ . Group | Group-Object -Property Source ) .Length -gt 1 } Get-Command | Group-Object -Property Name | where { $_.Count -GT 1 -and ($_.Group | Group-Object -Property Source).Length -gt 1 }

To display the groups, I use the Format-Table cmdlet with a calculated property for the Source value.

$diftModules = Get-Command | Group-Object -Property Name | where { $_ .Count -GT 1 -and ( $_ . Group | Group-Object -Property Source ) .Length -gt 1 } $diftModules | Format-Table -Property Name , @ { Label = 'Module' ; Expression = { $_ . Group .Source } } $diftModules = Get-Command | Group-Object -Property Name | where { $_.Count -GT 1 -and ($_.Group | Group-Object -Property Source).Length -gt 1 } $diftModules | Format-Table -Property Name, @{Label = 'Module'; Expression = { $_.Group.Source } }

Which command runs?

Now, that we can find commands with the same name, I’d like to know which command runs by default when I invoke that command without a qualifying module or version. The answer is not some new-fangled cmdlet. It’s good old Get-Command.

When the value of the Name parameter includes wildcard characters (*), Get-Command gets all commands with the specified name. But, when the value of the Name parameter has no wildcards, Get-Command gets the command that runs by default; the one that takes precedence.

For example, using a wildcard character (*) in Name, you can see the two Set-Clipboard commands that Group-Object detected.

PS C:\> Get-Command Set-Clipboard* CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Set-Clipboard 3.1.0.0 Microsoft.PowerShell.Management Cmdlet Set-Clipboard 3.2.1.0 Pscx

To find the one that runs by default, run Get-Command with no wildcard characters. It’s the newest version in the user-specific module directory.

PS C:\> Get-Command Set-Clipboard CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Set-Clipboard 3.1.0.0 Microsoft.PowerShell.Management

This is very helpful. But, what do you do to make sure that you’re running the command that you want to run? Stay tuned for a series of posts on different strategies to avoid mistakes.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at juneb@sapien.com and follow her on Twitter at @juneb_get_help.