Powershell: Some parameters are not provided if : is inside the string for at least LiteralPath, Path

Created on 12 Feb 2020  路  8Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

Type:

 get-content -LiteralPath 'c:\test.html'

Continue typing - and press CTRL+SPACE

 get-content -LiteralPath 'c:\test.html' -

image

Notice how RAW is not on the list. If you will choose -R and ctrl+space it will work

image

It behaves like that for all PowerShell versions losing parameters inside. If you don't use : inside the string:

get-content -LiteralPath 'c\test.html'

image

All parameters will be provided. The workaround for this is to type RAW first. I've seen it multiple places not just for get-content where some parameters are missing from parameterset. I was able to track it down to the : being a problem. It may be strictly related to Path/LiteralPath.

image

Expected behavior

Should not matter what is inside the string, at least I think it shouldn't. Otherwise, all paths on windows break it.

Environment data

Name                           Value
----                           -----
PSVersion                      7.0.0-rc.2
PSEdition                      Core
GitCommitId                    7.0.0-rc.2
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Issue-Bug

Most helpful comment

Distilled repro:

$script = 'Get-Content -Path "C:\test" -'
TabExpansion2 -inputScript $script -cursorColumn $script.Length |
    % { $_.CompletionMatches.CompletionText } |
    Should -Contain '-Raw'

Interesting to note that these pass:

# Bare word argument
Get-Content -Path C:\test -

# No drive separator character.  Position in string doesn't matter, always fails if it's present
Get-Content -Path "C\test" -

All 8 comments

Distilled repro:

$script = 'Get-Content -Path "C:\test" -'
TabExpansion2 -inputScript $script -cursorColumn $script.Length |
    % { $_.CompletionMatches.CompletionText } |
    Should -Contain '-Raw'

Interesting to note that these pass:

# Bare word argument
Get-Content -Path C:\test -

# No drive separator character.  Position in string doesn't matter, always fails if it's present
Get-Content -Path "C\test" -

-Raw is a dynamic parameter added by the PS file-system provider, so by design it should only be offered if the Get-Content command can be inferred to be targeting a _file-system_ location.

Here's how it _mostly_ works at the moment - except for the bug:

  • If the path starts with a reference to a _valid_ drive (e.g, C:, env:), the target provider is inferred from that.

    • Note that it is only the _drive_'s existence that is considered; whether the rest of the path exists is irrelevant.
  • If the path refers to a _non-existent_ drive, _no_ provider's dynamic parameters are being offered.

  • If there is _no path at all_, or the path _doesn't start with a drive spec_ - including if it starts with a _variable reference_ - completion assumes the _provider underlying the current location_ is being targeted (which explains why Get-Content -Path "C\test" - works, as long as the current location is a file-system one).

    • Caveat re Unix-like platforms: native file-system paths do not use drive specs there, so you cannot _formally_ infer from something like /tmp that it refers to a file-system location, and the current behavior appears to be to _always_ defer to the _current location's_ provider - even if the path exists as a file-system path. In effect, on Unix-like platforms _any_ path that doesn't start with a drive spec defers to the current location's provider.

Aided by your repro, @SeeminglyScience, I think the bug is specific to the following scenario:

  • If you type _only_ -, and a path that does include a valid file-system drive spec _that is also quoted_ (whether singly or doubly) is specified, it is seemingly not recognized as a valid _file-system_ path, causing the file-system-provider-specific -Raw switch _not_ to be offered among the completions. Curiously, typing -r makes the bug go away, but typing -ra makes it reappear.

So it seems like it's reversed now. It only shows up if there is no -Path/-LiteralPath used. It also shows if no drive is specified. It disappears as soon as the drive is specified. The bug is in 5.1,6+,7+

It also shows if no drive is specified

Only if the current location is a _file-system_ location (which, in fairness, is by far the most common scenario). If you do Set-Location env: first, you'll see that it disappears - as it should (see explanation above).

It disappears as soon as the drive is specified.

Only if the path is _quoted_ - that is the bug.

My guess is that the problem is here:

https://github.com/PowerShell/PowerShell/blob/a578347b5a9d4b7c48f2cd303f876dfa6f27cdce/src/System.Management.Automation/engine/CommandCompletion/PseudoParameterBinder.cs#L1237-L1243

The argument should be added as ((StringConstantExpression)ast).Value instead of as ast.Extent.Text.

That's promising, @SeeminglyScience, but it won't work with VariableExpressionAst (e.g, $var) and ExpandableStringExpressionAst (e.g, $var\sub).

Perhaps simply trimming the quotes explicitly is the solution?

var unquotedText = System.Text.RegularExpressions.Regex.Replace(
  expressionArgument.Extent.Text, 
  @"^(['""])(.*)\1$", "$2"
);
argumentsToGetDynamicParameters?.Add(unquotedText);

(I've had to amend the previous comment yet again: a non-existent drive indeed causes _no_ provider's dynamic parameters to expand (which is sensible) - except for the potentially distinct bug described in #11848, which is where my confusion stemmed from.)

imo just get dynamic parameters based on current location if it's an expandable string.

If you wanted to go the extra mile you could maybe use the SafeExprEvaluator but that might be overkill. Either way, using Extent.Text has more issues than the quotes. It won't translate ` or special character sequences using them either. Plus here-strings...etc... trying to evaluate based on extent text gets dicey quick.

imo just get dynamic parameters based on current location if it's an expandable string.

That's certainly one way to resolve it, but it would misbehave in cases such as OtherProviderDrive:\sub\$foo

Good point that merely trimming enclosing quotes from Extent.Text isn't fully robust, but I feel it would cover more scenarios than simply falling back to the current location's provider in the presence of _any_ expandable string: `-escaping or escape sequences are unlikely to occur in the drive-spec portion of a path, and use of here-strings for path arguments is similarly unlikely.

Either approach would certainly be an improvement, though.

(Knowing nothing about SafeExprEvaluator, I can't speak to how much more effort a robust solution would require.)

Was this page helpful?
0 / 5 - 0 ratings