如何在 Powershell 中分配和引用包含方括号的环境变量 [英] How to assign and reference environment variables containing square brackets in Powershell
问题描述
当未指定 PSDrive 时,以下工作:
When the PSDrive is not specified, the following works:
${[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
以下工作,虽然我很好奇它是否有简写语法:
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
推荐答案
从 PowerShell Core 6.2.0 开始编写
原因是 PowerShell 处理如下:
The reason is that PowerShell treats the following:
${<drive>:<name>}
好像你已经指定了:
Get-Content -Path <drive>:<name> # or, with assignment, Set-Content -Path ...
这种表示法 - 虽然经常与 Env:
驱动器一起使用(例如,$env:Path
) - 但作为一种通用范式鲜为人知em> 命名为命名空间变量表示法,这在this answer.
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.
问题是使用-Path
而不是-LiteralPath
,因为-Path
将其参数解释为通配符表达式.
The problem is the use of -Path
rather than -LiteralPath
, because -Path
interprets its argument as a wildcard expression.
因此,${env:[foo]}
中的 [foo]
- 而不是按原样使用 - 被解释为匹配单个字符是 f
或 o
([foo]
是字符集或范围([...]
) 匹配其中的任何一个(不同的)字符 - 请参阅 about_Wildcards).
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).
在分配到${env:[foo]}
时,Set-Content -Path
的逻辑需要一个基于通配符的路径解析为存在的东西,即使您通常不需要显式创建环境变量;例如,${env:NoSuchVarExistsYet} = 'new'
工作得很好.
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.
解决方法:
使用 double(!)-`
-转义通配符元字符:
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``]}
注意:
根本不需要转义,因为没有充分的理由将在概念上标识给定已知项目的路径视为通配符表达式 - 请参阅 GitHub 问题 #9225.
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.
需要 double `
-escaping 是一个额外的怪癖 - 请参阅 GitHub 问题 #7999.
That double `
-escaping is needed is an added quirk - see GitHub issue #7999.
另一种解决方法 - 不涉及转义 - 是使用 Set-Content -LiteralPath env:[foo] bar
和 Get-Content -LiteralPath env:[foo]
,但这既冗长又慢.
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.
至于你尝试过的其他语法变化:
$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.
{...}
——封闭整个路径——${env:[foo]}
——解决语法问题,但是遇到了上面详述的问题.
{...}
-enclosing the entire path -${env:[foo]}
- solves the syntax problem, but runs into the problem detailed above.
Set-Item -LiteralPath env:${[foo]} -Value "bar"
这 not 通常会起作用,因为这里预先应用了 字符串扩展 - 就好像你已经通过了 "env:${[foo]}"
:对名为 ${[foo]}
的(常规)变量的引用被 扩展(替换为它的值)并且实际上是附加的到文字 env:
, before 将结果交给 Set-Item
.
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
.
如果这样的正则变量不存在,Set-Item
看到的只是env:
(因为不存在的变量默认为$null
,成为字符串上下文中的空字符串),由于缺少变量名而导致错误.
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.
相比之下,下面会设置一个名为 unrelated
的环境变量:
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'
同样适用于 Get-Item -LiteralPath env:${[foo]}
和 Set-Item -LiteralPath env:${[foo]2} -Value "bar"
.
这篇关于如何在 Powershell 中分配和引用包含方括号的环境变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!