如何在Powershell中分配和引用包含方括号的环境变量

cdm*_*hai 2 syntax powershell namespaces environment-variables

未指定PSDrive时,以下工作:

${[foo]}="bar"
echo ${[foo]}
Run Code Online (Sandbox Code Playgroud)

但是以下方法不起作用

$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
Run Code Online (Sandbox Code Playgroud)
${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
Run Code Online (Sandbox Code Playgroud)

下面的作品,虽然我很好奇是否有简短的语法:

Set-Item -LiteralPath env:${[foo]} -Value "bar"
Get-Item -LiteralPath env:${[foo]} | % {$_.Value}
Run Code Online (Sandbox Code Playgroud)

但是,以下操作无效:

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
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

从PowerShell Core 6.2.0起编写

原因是PowerShell处理以下内容:

${<drive>:<name>}
Run Code Online (Sandbox Code Playgroud)

就像您已指定:

Get-Content -Path <drive>:<name>  # or, with assignment, Set-Content -Path ...
Run Code Online (Sandbox Code Playgroud)

这种表示法-尽管经常与Env:驱动器(例如$env:Path)一起使用-鲜为人知,它是名为命名空间变量表示法通用范式对此答案进行了解释。

问题是使用-Path而不是-LiteralPath,因为-Path 将其参数解释为通配符表达式

因此,[foo]in ${env:[foo]}(而不是按原样使用)被解释为通配符表达式,该通配符表达式与一个字符匹配,该单个字符为fo[foo]是字符集或范围([...]),该字符与内部的任何(不同)字符匹配-请参见about_Wildcards)。

分配${env:[foo]}的逻辑Set-Content -Path要求,基于通配符路径的决心,一些现有的,即使你通常要求明确创建的环境变量; 例如,${env:NoSuchVarExistsYet} = 'new'效果很好。


解决方法

使用(!)- `转义通配符

# 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``]}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 根本不需要转义,因为没有充分的理由将概念上标识给定的已知项目的路径称为通配符表达式-请参阅GitHub问题

  • ` -escaping需要的是一个额外的怪癖-看到这个GitHub的问题

  • 另一种解决方法 -不涉及转义-使用
    Set-Content -LiteralPath env:[foo] barand Get-Content -LiteralPath env:[foo],但这既冗长又缓慢。


至于您尝试的其他语法变化

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

由于您的变量引用不是整体-{...}括起来(除了initial $,所以)之后的标记:仅允许包含不需要转义的字符-和${并且}都违反了该规则。

  • {...}-包含整个路径- ${env:[foo]}解决了语法问题,但遇到了上面详述的问题。

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

这确实不是工作在一般情况下,因为串扩展应用于预先在这里-这是因为如果你已通过"env:${[foo]}":到一个名为(常规)变量的引用${[foo]}扩展(替代其值),在附加文字效果env:之前移交的结果到Set-Item

如果不存在这样的常规变量,Set-Item则只能看到env:(因为不存在的变量默认为$null,它在字符串上下文中成为空字符串),由于缺少变量名而导致错误。

相比之下,以下将设置一个名为的环境变量unrelated

# 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'
Run Code Online (Sandbox Code Playgroud)

Get-Item -LiteralPath env:${[foo]}和和相同
Set-Item -LiteralPath env:${[foo]2} -Value "bar"