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

问题描述 投票:2回答:1

未指定PSDrive时,以下工作:

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

但以下不起作用

$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

以下是有效的,但我很好奇是否有简短的语法:

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

但是以下不起作用:

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
powershell syntax namespaces environment-variables
1个回答
5
投票

写自PowerShell Core 6.2.0

原因是PowerShell处理以下内容:

${<drive>:<name>}

好像你已经指定:

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

这种表示法 - 虽然经常与Env:驱动器一起使用(例如,$env:Path) - 鲜为人知,但它是一种名为命名空间变量表示法的通用范例,在this answer中对此进行了解释。

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

因此,[foo]中的${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``]}

注意:

  • 根本不需要转义,因为没有充分的理由将概念上标识给定的已知项目的路径视为通配符表达式 - 请参阅this GitHub issue
  • 需要双`逃脱是一个额外的怪癖 - 见this GitHub issue
  • 另一个解决方法 - 一个不涉及转义的解决方法 - 是使用 Set-Content -LiteralPath env:[foo] barGet-Content -LiteralPath env:[foo],但这既冗长又缓慢。

至于您尝试的其他语法变体:

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

由于你的变量引用不是{...}整体封闭(除了最初的$),:后面的标记只允许包含不需要转义的字符 - 而${}都违反了该规则。

  • {...}-包围整个路径 - ${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'

这同样适用于Get-Item -LiteralPath env:${[foo]}Set-Item -LiteralPath env:${[foo]2} -Value "bar"

© www.soinside.com 2019 - 2024. All rights reserved.