变量扩展与属性表达式
PowerShell 解析器以不同方式解释字符串中的字符串、变量扩展和属性表达式。这种差异改变了参数列表的生成方式和传递给可执行文件的方式。
这就是生成参数列表的原因:
# By Object Property Access
docker --tag $imageInfo.FullImageName`:$version
# -- Doesn't work --
# By Variable Expansion
docker --tag $imageInfo`:$version
# -- Works --
# By Sub Expression Operator
docker --tag $($imageInfo.FullImageName)`:$version
# -- Doesn't work --
# By Double quoted string + Sub Expression Operator
docker --tag "$($imageInfo.FullImageName):$version"
# -- Works --
0 级:TLDR
您必须使用子表达式运算符$() 的组合来访问对象属性,和 将整个字符串括在双引号中以便正确解释。这也消除了逃避反勾号的需要。所以完整的解决方案是:
docker image build --tag "$($imageInfo.FullImageName):$version" --file .\Dockerfile .
第 1 级:解释参数
PowerShell 首先通过它自己的语法解析器运行命令,然后再将参数传递给外部程序,例如因此它可以执行变量替换等。在高层次上,我们可以看到 PowerShell 如何解释命令以及通过运行工具 EchoArgs.exe 传递了哪些参数,并回显传递了哪些参数(我已经简化了示例以说明什么是继续):
#Setup Variables for MCVE:
$imageInfo = New-Object -TypeName psobject
$imageInfo | Add-Member -MemberType NoteProperty -Name FullImageName -Value "ImageName"
$strImageName = "ImageName"
$version = "v1.2.3"
按对象属性访问
PS C:\> EchoArgs.exe docker --tag $imageInfo.FullImageName`:$version
Arg 0 is <docker>
Arg 1 is <--tag>
Arg 2 is <ImageName>
Arg 3 is <:v1.2.3>
Command line:
"C:\EchoArgs.exe" docker --tag ImageName :v1.2.3
在这里我们可以看到 $imageInfo.FullImageName 被评估并作为一个参数 (Arg 2) 传递,并注意到 冒号和版本字符串正在组合并被解释为一个新参数 (参数 3)。然后,这会导致将执行的最终生成的命令行在图像名称和版本之间有一个空格:ImageName :v1.2.3
这显然会混淆 docker 和任何其他类似的可执行文件。
通过变量展开
PS C:\> EchoArgs.exe docker --tag $strImageName`:$version
Arg 0 is <docker>
Arg 1 is <--tag>
Arg 2 is <ImageName:v1.2.3>
Command line:
"C:\EchoArgs.exe" docker --tag ImageName:v1.2.3
使用字符串变量,我们可以看到参数被正确解释。它扩展了字符串变量并将所有内容正确地保存在一起作为一个参数(Arg 2)。
按子表达式运算符
PS C:\> EchoArgs.exe docker --tag $($imageInfo.FullImageName)`:$version
Arg 0 is <docker>
Arg 1 is <--tag>
Arg 2 is <ImageName>
Arg 3 is <:v1.2.3>
Command line:
"C:\EchoArgs.exe" docker --tag ImageName :v1.2.3
与第一个示例类似,我们可以看到 $($imageInfo.FullImageName) 被评估并作为一个参数 (Arg 2) 传递,并且冒号和版本字符串被解释为一个新参数 (Arg 3)。这不起作用。
双引号字符串+子表达式运算符
PS C:\> EchoArgs.exe docker --tag "$($imageInfo.FullImageName):$version"
Arg 0 is <docker>
Arg 1 is <--tag>
Arg 2 is <ImageName:v1.2.3>
Command line:
"C:\EchoArgs.exe" docker --tag ImageName:v1.2.3
在这种情况下,双引号包含整个字符串。子表达式 $($imageInfo.FullImageName) 首先被评估。因为冒号和$version 字符串也包含在相同的双引号中,所以在作为单个参数(Arg 2)返回之前,必须对整个字符串进行完全评估。
第 2 级:抽象语法树
我以为$imageInfo.FullImageName 和$strImageName
都是字符串,为什么要区别对待呢?
我们可以使用EchoArgs.exe 来查看它们的处理方式不同,但要有效地回答这个问题,我们需要了解 PowerShell 如何解析命令。毕竟,如果我们知道这是一个 PowerShell 解析器问题,那么解析器如何解释我们正在做的事情?
不要尝试在 PowerShell 中解析 PowerShell 代码
改用 PowerShell 解析器!
让我们使用 PowerShell 自己的抽象语法树来解释发生了什么!
按对象属性访问
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseInput('docker --tag $imageInfo.FullImageName`:$version', [ref]$null, [ref]$null)
$CommandAST = $ScriptAST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
$CommandAST.CommandElements
StringConstantType : BareWord
Value : docker
StaticType : System.String
Extent : docker
Parent : docker --tag $imageInfo.FullImageName`:$version
StringConstantType : BareWord
Value : --tag
StaticType : System.String
Extent : --tag
Parent : docker --tag $imageInfo.FullImageName`:$version
Expression : $imageInfo
Member : FullImageName
Static : False
StaticType : System.Object
Extent : $imageInfo.FullImageName
Parent : docker --tag $imageInfo.FullImageName`:$version
Value : :$version
StringConstantType : BareWord
NestedExpressions : {$version}
StaticType : System.String
Extent : `:$version
Parent : docker --tag $imageInfo.FullImageName`:$version
在这里我们看到docker 和--tag 被解释为StringConstantType : BareWord 令牌。 $imageInfo.FullImageName 被解释为 Expression : $imageInfo,需要对其进行评估。最后,:$version 也被解释为StringConstantType : BareWord。这直接对应于我们之前看到的 4 个参数。那么,一个普通的变量呢?
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseInput('docker --tag $strImageName`:$version', [ref]$null, [ref]$null)
$CommandAST = $ScriptAST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
$CommandAST.CommandElements
StringConstantType : BareWord
Value : docker
StaticType : System.String
Extent : docker
Parent : docker --tag $strImageName`:$version
StringConstantType : BareWord
Value : --tag
StaticType : System.String
Extent : --tag
Parent : docker --tag $strImageName`:$version
Value : $strImageName:$version
StringConstantType : BareWord
NestedExpressions : {$strImageName, $version}
StaticType : System.String
Extent : $strImageName`:$version
Parent : docker --tag $strImageName`:$version
那是不同的。它将$strImageName:$version 视为单个StringConstantType : BareWord 令牌类型。 PowerShell 了解如何处理简单的变量字符串扩展并仍然返回单个字符串对象。也就是说,所有变量字符串都是StaticType : System.String,它们都可以组合成一个,而不是前面的例子,我们有一个StaticType : System.Object和一个System.String,虽然在下一步变成字符串,但它们在这里是不同的类型,因此不会合并。
按子表达式运算符
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseInput('docker --tag $($imageInfo.FullImageName)`:$version', [ref]$null, [ref]$null)
$CommandAST = $ScriptAST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
$CommandAST.CommandElements
StringConstantType : BareWord
Value : docker
StaticType : System.String
Extent : docker
Parent : docker --tag $($imageInfo.FullImageName)`:$version
StringConstantType : BareWord
Value : --tag
StaticType : System.String
Extent : --tag
Parent : docker --tag $($imageInfo.FullImageName)`:$version
SubExpression : $imageInfo.FullImageName
StaticType : System.Object
Extent : $($imageInfo.FullImageName)
Parent : docker --tag $($imageInfo.FullImageName)`:$version
Value : :$version
StringConstantType : BareWord
NestedExpressions : {$version}
StaticType : System.String
Extent : `:$version
Parent : docker --tag $($imageInfo.FullImageName)`:$version
SubExpression : $imageInfo.FullImageName 被解释为 StaticType : System.Object,不能与 System.String 结合使用。
双引号字符串+子表达式运算符
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseInput('docker --tag "$($imageInfo.FullImageName):$version"', [ref]$null, [ref]$null)
$CommandAST = $ScriptAST.FindAll({$args[0] -is [System.Management.Automation.Language.CommandAst]}, $true)
$CommandAST.CommandElements
StringConstantType : BareWord
Value : docker
StaticType : System.String
Extent : docker
Parent : docker --tag "$($imageInfo.FullImageName):$version"
StringConstantType : BareWord
Value : --tag
StaticType : System.String
Extent : --tag
Parent : docker --tag "$($imageInfo.FullImageName):$version"
Value : $($imageInfo.FullImageName):$version
StringConstantType : DoubleQuoted
NestedExpressions : {$($imageInfo.FullImageName), $version}
StaticType : System.String
Extent : "$($imageInfo.FullImageName):$version"
Parent : docker --tag "$($imageInfo.FullImageName):$version"
在这里我们看到了不同之处。 "$($imageInfo.FullImageName):$version" 被解释为单个 StringConstantType : DoubleQuoted 字符串标记。它确实有嵌套表达式,但它被解析为单个标记,因此将作为单个参数返回。