Prefer literal Select-Object property matches#27515
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates Select-Object to correctly handle literal property names that include wildcard characters, and adds Pester tests covering the new resolution behavior.
Changes:
- Prefer exact (literal) property-name matches before performing wildcard expansion for
-Property. - Apply the same literal-name handling to
-ExpandProperty. - Add tests covering escaped wildcard characters and exact-match precedence.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 | Adds test coverage for literal property names containing wildcard characters and exact-match precedence. |
| src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs | Changes property resolution to detect and select exact property names (including wildcard characters) before wildcard expansion. |
| return inputObject.Properties[propertyName] is null | ||
| ? null | ||
| : new PSPropertyExpression(propertyName, isResolved: true); |
There was a problem hiding this comment.
Addressed in 2ce27ae3d: literal escaped property resolution now looks up the matched member and builds the resolved expression from member.Name, preserving the canonical member casing in output.
| PSPropertyExpression literalExpression = GetLiteralPropertyExpression(expression, inputObject); | ||
| return literalExpression is null | ||
| ? expression.ResolveNames(inputObject) | ||
| : new List<PSPropertyExpression> { literalExpression }; | ||
| } | ||
|
|
||
| private static List<PSPropertyExpressionResult> GetPropertyValues(PSPropertyExpression expression, PSObject inputObject) | ||
| { | ||
| PSPropertyExpression literalExpression = GetLiteralPropertyExpression(expression, inputObject); | ||
| return literalExpression is null | ||
| ? expression.GetValues(inputObject) | ||
| : literalExpression.GetValues(inputObject); | ||
| } | ||
|
|
||
| private static PSPropertyExpression GetLiteralPropertyExpression(PSPropertyExpression expression, PSObject inputObject) | ||
| { | ||
| if (expression.Script is not null) | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| string propertyName = WildcardPattern.Unescape(expression.ToString()); | ||
| return inputObject.Properties[propertyName] is null | ||
| ? null | ||
| : new PSPropertyExpression(propertyName, isResolved: true); |
There was a problem hiding this comment.
Addressed in 2ce27ae3d: GetPropertyValues now reuses ResolvePropertyNames, so the literal-then-fallback flow is centralized instead of duplicated between the two call sites.
| private static List<PSPropertyExpression> ResolvePropertyNames(PSPropertyExpression expression, PSObject inputObject) | ||
| { | ||
| PSPropertyExpression literalExpression = GetLiteralPropertyExpression(expression, inputObject); | ||
| return literalExpression is null | ||
| ? expression.ResolveNames(inputObject) | ||
| : new List<PSPropertyExpression> { literalExpression }; | ||
| } |
There was a problem hiding this comment.
Addressed in 2ce27ae3d: unescaped wildcard expressions now keep the existing wildcard expansion behavior. Literal resolution only applies when the caller used an escaped wildcard property name, so Select-Object -Property 'Foo*' still expands matching properties.
PR Summary
Makes
Select-Objectprefer an exact property-name match before treating wildcard-looking property arguments as wildcard patterns.PR Context
Fix #25982.
The cmdlet currently treats property names containing wildcard characters only as wildcard patterns, even when the input object has a property whose literal name matches the requested value. This means a generated object with a property such as
Foo[]cannot reliably select or expand that property even when the caller uses[WildcardPattern]::Escape().This change checks for a literal property match first, using the wildcard-unescaped property expression where applicable. If no exact property exists, existing wildcard expansion behavior is preserved.
Regression tests cover:
Foo[]Foo*taking precedence over wildcard expansion-ExpandPropertywith escaped literal property namesPR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerValidation
Start-PSBuild -NoPSModuleRestore -CI -SkipExperimentalFeatureGeneration -UseNuGetOrg[WildcardPattern]::Escape('Foo[]')succeeds for both-Propertyand-ExpandProperty.Start-PSPester -Path test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 -UseNuGetOrg -ThrowOnFailure -Terse -SkipTestToolBuildResult: Select-Object.Tests.ps1 passed: 54 passed, 0 failed.