未指定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 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]}
- 而不是被用作 - 是 - 被解释为匹配单个字符的通配符表达式,该字符是f
或o
([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。Set-Content -LiteralPath env:[foo] bar
和Get-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"
。