Fix hyphen-prefixed arguments being split at period #26522
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
PR Summary
Fix argument parsing for hyphen-prefixed tokens containing periods. Tokens like
-foo.barare now passed as a single argument instead of being incorrectly split into-fooand.bar.PR Context
Fixes #6291
Also partially addresses the splatting issue described in #6360 (closed due to inactivity, not because it was fixed).
Note: This PR fixes the period (
.) splitting issue in splatting, but does not fix the colon (:) splitting issue also mentioned in #6360. The colon case requires a different fix in NativeCommandParameterBinder.The Problem
When passing unquoted arguments that start with a hyphen and contain a period, PowerShell incorrectly splits them at the first period:
This behavior affects many real-world scenarios, particularly when calling external programs with flags like:
-DVERSION=1.2.3(compiler defines)-std=c++20(compiler standards)-fmodule-file=Hello=Hello.pcm(clang modules)--config=path/to.file(various CLI tools)Committee Decision
The PowerShell Committee reviewed this issue and confirmed it is a bug:
Before this fix:
After this fix:
Technical Approach
Root Cause
In
ScanParameter()method oftokenizer.cs, when the tokenizer encounters a hyphen at the start of a token, it begins scanning for a parameter name. When it encounters a period (.), it terminates the parameter scan and leaves the remaining text (.bar) as a separate token.This behavior was originally designed for expression mode where
.indicates member access (e.g.,$obj.Property). However, in command mode, this special treatment of.causes unintended argument splitting.Solution
Modified the
case '.'handling inScanParameter()to check if we're in command mode. When in command mode:-and everything after the.) is treated as a generic argumentScanGenericToken()which preserves it as a single unitThis approach mirrors the existing handling of quote characters (
',") in the same method, which already use the same pattern to treat quoted content as arguments rather than parameters.Backward Compatibility Analysis
Why This Change Is Safe
Parameter names cannot contain periods: PowerShell does not allow
.in parameter names. Any attempt to declareparam($foo.bar)results in a parse error.No intentional usage exists: Since
-foo.barcannot match any declared parameter, the current "split at period" behavior is purely accidental. No scripts intentionally rely on-foo.barbeing interpreted as-foowith value.bar.Consistent with user expectations: Users who write
-foo.barexpect it to be a single argument (typically for external programs).Colon syntax remains unchanged: The intentional
-param:valuesyntax continues to work as expected.Verified Backward Compatibility
The following scenarios were tested and continue to work correctly:
-foo bar)-foo:bar)-Verbose)$obj.foo)'hello'.ToUpper())-3.14)-3..-1)--foo.bar)Potential Concerns
WG-Engine's Comment
WG-Engine commented:
This PR applies the fix to all commands (not just native commands) because:
Tokenization happens before command resolution: At the tokenizer level, PowerShell doesn't know whether the target is a native command, cmdlet, or function.
No practical difference: Since parameter names cannot contain periods, the "split at period" behavior never provides useful functionality for PowerShell commands either.
Splatting consistency: The fix ensures that
$Argsand@Argscorrectly preserve arguments regardless of whether they're ultimately passed to native commands or PowerShell functions.Extensive testing: All backward compatibility scenarios have been verified with no regressions found.
The PowerShell team should evaluate whether this broader fix is acceptable or if a native-command-only approach is required.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerTest Coverage
Added 25 comprehensive test cases covering:
Basic Cases
-foo.bar- single period-foo=bar.baz- equals sign with periodEdge Cases: Multiple and Consecutive Periods
-foo..bar- consecutive periods-foo...bar- three consecutive periods-foo.bar.baz- multiple periods-foo=1.2.3.4- IP-address-like patternEdge Cases: Leading and Trailing Periods
-.foo- leading period after hyphen-foo.- trailing periodDouble Hyphen
--foo.bar- GNU-style long option (already worked, included for completeness)Real-World Use Cases
-DVERSION=1.2.3- compiler define-std=c++20- compiler standard flagSplatting
@Argswith-foo.bar@Argswith-foo=bar.bazNative Commands
Backward Compatibility
-foo:bar)-Path .txt)Test Results
Files Changed
src/System.Management.Automation/engine/parser/tokenizer.cs- Modified period handling inScanParameter()test/powershell/Language/Parser/Parser.Tests.ps1- Added comprehensive test coverage