diff --git a/src/System.Management.Automation/engine/GetCommandCommand.cs b/src/System.Management.Automation/engine/GetCommandCommand.cs index 10e833402..37ff83ced 100644 --- a/src/System.Management.Automation/engine/GetCommandCommand.cs +++ b/src/System.Management.Automation/engine/GetCommandCommand.cs @@ -886,10 +886,9 @@ namespace Microsoft.PowerShell.Commands { if (TotalCount < 0 || count < TotalCount) { - IEnumerable commands; if (UseFuzzyMatching) { - foreach (var commandScore in ModuleUtils.GetFuzzyMatchingCommands( + foreach (CommandScore commandScore in ModuleUtils.GetFuzzyMatchingCommands( plainCommandName, Context, MyInvocation.CommandOrigin, @@ -897,37 +896,48 @@ namespace Microsoft.PowerShell.Commands rediscoverImportedModules: true, moduleVersionRequired: _isFullyQualifiedModuleSpecified)) { - _commandScores.Add(commandScore); - } + CommandInfo current = commandScore.Command; + if (IsCommandMatch(ref current, out isDuplicate) && !IsCommandInResult(current) && IsParameterMatch(current)) + { + _accumulatedResults.Add(current); + _commandScores.Add(new CommandScore(current, commandScore.Score)); - commands = _commandScores.Select(static x => x.Command); + // Make sure we don't exceed the TotalCount parameter + ++count; + + if (TotalCount >= 0 && count >= TotalCount) + { + break; + } + } + } } else { - commands = ModuleUtils.GetMatchingCommands( + IEnumerable commands = ModuleUtils.GetMatchingCommands( plainCommandName, Context, MyInvocation.CommandOrigin, rediscoverImportedModules: true, moduleVersionRequired: _isFullyQualifiedModuleSpecified, useAbbreviationExpansion: UseAbbreviationExpansion); - } - foreach (CommandInfo command in commands) - { - // Cannot pass in "command" by ref (foreach iteration variable) - CommandInfo current = command; - - if (IsCommandMatch(ref current, out isDuplicate) && (!IsCommandInResult(current)) && IsParameterMatch(current)) + foreach (CommandInfo command in commands) { - _accumulatedResults.Add(current); + // Cannot pass in "command" by ref (foreach iteration variable) + CommandInfo current = command; - // Make sure we don't exceed the TotalCount parameter - ++count; - - if (TotalCount >= 0 && count >= TotalCount) + if (IsCommandMatch(ref current, out isDuplicate) && (!IsCommandInResult(current)) && IsParameterMatch(current)) { - break; + _accumulatedResults.Add(current); + + // Make sure we don't exceed the TotalCount parameter + ++count; + + if (TotalCount >= 0 && count >= TotalCount) + { + break; + } } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Command.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Command.Tests.ps1 index a07ed78e7..766848c7a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Command.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-Command.Tests.ps1 @@ -29,6 +29,33 @@ Describe "Get-Command Feature tests" -Tag Feature { $cmd.Score | Should -BeLessOrEqual 3 } } + + It "Should not return duplicate commands from multiple copies of a module when using -UseFuzzyMatching" { + # Create two discoverable copies of the same module so the duplicate-candidate scenario + # is reproduced consistently without relying on platform-specific installed modules. + $moduleRoots = 1..2 | ForEach-Object { + $moduleRoot = Join-Path $TestDrive "ModuleRoot$_" + $modulePath = Join-Path $moduleRoot "DuplicateFuzzyModule" + $null = New-Item -ItemType Directory -Path $modulePath + New-ModuleManifest -Path (Join-Path $modulePath "DuplicateFuzzyModule.psd1") ` + -ModuleVersion "1.0" ` + -CmdletsToExport "Get-FuzzyDuplicate" + $moduleRoot + } + + $originalModulePath = $env:PSModulePath + try { + $env:PSModulePath = $moduleRoots -join [System.IO.Path]::PathSeparator + $cmds = Get-Command Get-FuzzyDuplicat -UseFuzzyMatching -FuzzyMinimumDistance 1 + } + finally { + $env:PSModulePath = $originalModulePath + } + + $cmds | Should -HaveCount 1 + $cmds.Name | Should -BeExactly "Get-FuzzyDuplicate" + $cmds.Score | Should -Be 1 + } } Context "-UseAbbreviationExpansion tests" {