Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
766 views
in Technique[技术] by (71.8m points)

syntax - How to assign and reference environment variables containing square brackets in Powershell

When the PSDrive is not specified, the following works:

${[foo]}="bar"
echo ${[foo]}

But the following does not work

$env:${[foo]}="bar"
At line:1 char:1 
+ $env:${[foo]}="bar"
+ ~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name.
At line:1 char:6
+ $env:${[foo]}="bar"
+      ~~~~~~~~~~~~~~
Unexpected token '${[foo]}="bar"' in expression or statement.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : InvalidVariableReferenceWithDrive
${env:[foo]}="bar"
Cannot find path 'env:[foo]' because it does not exist. 
At line:1 char:1
+ ${env:[foo]}="bar"
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (env:[foo]:String) [], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound

The following works, though I am curious if there's short hand syntax for it:

Set-Item -LiteralPath env:${[foo]} -Value "bar"
Get-Item -LiteralPath env:${[foo]} | % {$_.Value}

However the following does not work:

Set-Item -LiteralPath env:${[foo]2} -Value "bar"
Set-Item : Cannot process argument because the value of argument "name" is null. Change the value of argument "name" to a non-null value.      
At line:1 char:1
+ Set-Item -LiteralPath env:${[foo]2} -Value "bar"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:String) [Set-Item], PSArgumentNullException
    + FullyQualifiedErrorId : SetItemNullName,Microsoft.PowerShell.Commands.SetItemCommand
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Written as of PowerShell Core 6.2.0

The reason is that 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 answer.

The problem is the use of -Path rather than -LiteralPath, because -Path interprets its argument as a wildcard expression.

Therefore, the [foo] in ${env:[foo]} - rather than being used as-is - is interpreted as a wildcard expression that matches a single character that is either f or o ([foo] is a character set or range ([...]) that matches any one of the (distinct) characters inside - see about_Wildcards).

On assigning to ${env:[foo]}, the logic of Set-Content -Path requires that a wildcard-based path resolve to something existing, even though you're generally not required to explicitly create environment variables; e.g., ${env:NoSuchVarExistsYet} = 'new' works just fine.


Workaround:

Use double(!)-`-escaping of the wildcard metacharacters:

# Namespace variable notation only works with if you
# double(!)-backtick-escape the wildcard metacharacters:

# Assign to / implicitly create env. var '[foo]'
${env:``[foo``]} = 'bar'

# Get its value.
${env:``[foo``]}

Note:

  • Escaping shouldn't be required at all, because there is no good reason to treat paths that conceptually identify a given, known item as wildcard expressions - see GitHub issue #9225.

  • That double `-escaping is needed is an added quirk - see GitHub issue #7999.

  • Another workaround - one that doesn't involve escaping - is to use
    Set-Content -LiteralPath env:[foo] bar and Get-Content -LiteralPath env:[foo], but that is both verbose and slow.


As for the other syntax variations you tried:

$env:${[foo]}="bar"

Since your variable reference isn't {...}-enclosed as a whole (except for the initial $), the token that follows the : is only allowed to contain characters that do not require escaping - and $, { and } all violate that rule.

  • {...}-enclosing the entire path - ${env:[foo]} - solves the syntax problem, but runs into the problem detailed above.

Set-Item -LiteralPath env:${[foo]} -Value "bar"

This does not work in general, because string expansion is applied beforehand here - it is as if you had passed "env:${[foo]}": the reference to a (regular) variable named ${[foo]} is expanded (replaced with its value) and in effect appended to literal env:, before handing the result to Set-Item.

If such a regular variable doesn't exist, what Set-Item sees is just env: (because non-existent variables default to $null, which becomes the empty string in a string context), which causes an error due to the lack of variable name.

By contrast, the following would set an environment variable named unrelated instead:

# Create a regular variable literally named '[foo]'.
${[foo]} = 'unrelated'

# !! The following sets env:unrelated, i.e., env. var 'unrelated',
# !! due to the string expansion that is performed on the -LiteralPath
# !! argument up front.
Set-Item -LiteralPath env:${[foo]} bar

$env:unrelated # -> 'bar'

The same applies to Get-Item -LiteralPath env:${[foo]} and
Set-Item -LiteralPath env:${[foo]2} -Value "bar".


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...