【问题标题】:Powershell Object Fields in Commands命令中的 Powershell 对象字段
【发布时间】:2020-01-08 22:31:53
【问题描述】:

我有以下功能:

function CreateImage
{
    param (
        [string] $version = $(throw "-version is required")
    )

    $imageInfo = GetImageParamatersFromPath     

    docker image build --tag $imageInfo.FullImageName`:$version --file .\Dockerfile .
}

当我调用它时它会失败(docker 命令抱怨参数数量)。

但如果我将其更改为无法在 docker 调用中访问 $imageInfo.FullImageName,它就可以正常工作:

function CreateImage
{
    param (
        [string] $version = $(throw "-version is required")
    )

    $imageInfo = GetImageParamatersFromPath
    $imageInfo = $imageInfo.FullImageName       

    docker image build --tag $imageInfo`:$version --file .\Dockerfile .
}

所以,在我的实际 docker 命令中,我似乎没有正确访问 FullImageName 字段。

在 PowerShell 命令中访问字段的正确方法是什么?

其他信息: 这可能与版本前的冒号有关。如果我将第一个函数更改为具有以下 docker 命令:

docker image build --tag $imageInfo.FullImageName --file .\Dockerfile .

然后就可以了。 (虽然它使用 :latest 而不是我的版本(显然))。

所以这可能与两者的互动方式有关。

鉴于此,它尝试了这个:

docker image build --tag ${imageInfo}.{FullImageName}`:$version --file .\Dockerfile .

还有这个:

docker image build --tag ${imageInfo}.{FullImageName}:$version --file .\Dockerfile .

但两者都不起作用。

【问题讨论】:

    标签: powershell


    【解决方案1】:

    变量扩展与属性表达式

    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 字符串标记。它确实有嵌套表达式,但它被解析为单个标记,因此将作为单个参数返回。

    【讨论】:

    • 哇!这是史诗般的答案!感谢您提供如此多的细节并解释它为什么这样做!
    【解决方案2】:

    试试--tag $($imageInfo.FullImageName)`:$version 这应该告诉 PowerShell 在将其传递给 docker 之前评估该表达式。

    【讨论】:

    • 不幸的是,这仍然不起作用......我真的不明白为什么,因为错误消息没有给出失败的命令。
    • 在命令输出前将echo放在图像名称后面的行上(但列出了图像名称)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    相关资源
    最近更新 更多