Powershell: Namespace variable notation unexpectedly treats paths as wildcard expressions

Created on 25 Mar 2019  路  3Comments  路  Source: PowerShell/PowerShell

Conceptually related: #4726.

Note: Fixing this issue would technically be a breaking change.

PowerShell treats the following:

${<drive>:<name>}

as if you had specified:

Get-Content -Path <drive>:<name>  # or, with assignment, Set-Content -Path ...

This notation - though often used with the Env: drive (e.g., $env:Path) - is little-known as a general paradigm named _namespace variable notation_, which is explained in this SO answer.

The problem is the use of -Path rather than -LiteralPath (conceptually speaking), because -Path interprets its argument as a _wildcard expression_.

By design, you can only ever target a _single_ item with namespace notation and if you intentionally specify a wildcard that resolves to _multiple_ items, the statement _breaks_.

There is no good reason for namespace notation to treat paths as wildcard expressions to begin with, just as it makes no sense to do so for executable invocations and redirections (see #4726).

The fact that they are treated as wildcards causes problems with item names that _happen to look like_ wildcard expressions, in one of two ways:

  • You may not be able to create a new item, such as a new environment variable, at all.
  • Perhaps worse, you may inadvertently target a different, preexisting item.

Taking the example of wanting to assign to / query the value of an environment variable literally named [foo] with ${env:[foo]}, from this SO question:

Steps to reproduce

# Try to create env. var. literally named '[foo]'
${env:[foo]} = 'bar'
# Create a seemingly unrelated env. var. whose name happens to match
# [foo] *if interpreted as a wildcard expression*
$env:o = 'no'
# Get the value
${env:[foo]}

Expected behavior

bar

That is, implicit creation of the variable via namespace notation should succeed, as should subsequent retrieval.

Actual behavior

Cannot find path 'env:[foo]' because it does not exist.
no
  • That is, implicit creation of the variable failed, because the wildcard resolution of [foo] matched nothing and so there was effectively no variable name to assign to / create.

  • ${env:[foo]} unexpectedly retrieved the value of ${env:o}, because [foo] interpreted as a wildcard matches o.

The workaround is to use _double_ `-escaping: ${env:``[foo``]}; that the escape char. must be doubled is an additional quirk - see #7999

Environment data

PowerShell Core 6.2.0-rc.1
Windows PowerShell v5.1
Issue-Question WG-Engine-Providers

All 3 comments

Isn't the wildcard behavior a function of the provider? In this case the ENV: provider, but in the case of a drive letter, its the file system provider. Does PowerShell need to tell the provider to accept it as a 'literal' path? I have also run in to the wildcard situation using the ENV provider using the namespace convention, but I think I was testing with a *.

The above example also gives a different error if the wildcard resolved to multiple items.

Cannot process variable because variable path 'env:[foo]' resolved to multiple items. You can get or set the variable value only one item at a time.
At line:1 char:1
+ ${env:[foo]}
+ ~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [], PSArgumentException
+ FullyQualifiedErrorId : Argument

The double backticking seems peculiar, as it only seems to be accepted as demonstrated, so it appears to be on purpose. It seems to be special to just the namespace variable notation.

At this point I would be willing to bet somewhere someone had a use for wildcard paths with the namespace variable notation, and so that's why this exists.

I am thinking about if these notations should be added to the PowerShell/EditorSyntax PR that I have been working on. I suspect they only apply to certain providers, of which those providers could be redefined or changed so there is probably no consistent way to scope these specific notations.

Namespace variable notation applies to _all_ providers that have PSDrives available, but isn't usable with all of them. In order to be usable with this syntax, the provider has to implement IContentCmdletProvider (i.e., Get-Content has to be implemented for that provider).

It also won't resolve to the provider if the PSDrive name is already taken by an existing scope (e.g., a PSDrive named global won't work, because that's a defined scope name).

I think that this is most likely to be an accidental result of the default providers by default applying globbing unless it's specifically requested to process the path as literal. The fact that a path that does resolve to multiple items fails to do anything other than throw an error would appear to support that, I think. 馃槃

Thanks for the clarifications, @vexx32.

@msftrncs

At this point I would be willing to bet somewhere someone had a use for wildcard paths with the namespace variable notation, and so that's why this exists.

I think it's more likely _accidental_ (I'm speculating), perhaps dating back to a time _before_ the great -Path / -LiteralPath schism, when _only_ wildcard expressions existed.

Similarly unhelpful behavior (accepting a wildcard, but requiring resolution to a single item):

  • has just been corrected in #9202, where it turned out to be a security concern

  • a fix for the related #4726 is pending.

  • a decision was recently made to complement switch -File with switch -LiteralFile - see #8988

Was this page helpful?
0 / 5 - 0 ratings