Поля объекта Powershell в командах - PullRequest
2 голосов
/ 09 января 2020

У меня есть следующая функция:

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

    $imageInfo = GetImageParamatersFromPath     

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

Когда я вызываю это, происходит сбой (команда docker жалуется на количество параметров).

Но если я изменил его, чтобы я не получил доступ к $imageInfo.FullImageName в вызове docker, он работает нормально:

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

    $imageInfo = GetImageParamatersFromPath
    $imageInfo = $imageInfo.FullImageName       

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

Так что, похоже, я не обращаюсь к FullImageName поле правильно в моей фактической команде docker.

Как правильно получить доступ к полю в команде PowerShell?

Дополнительная информация : Это может иметь отношение к двоеточию до версии. Если я изменю первую функцию, чтобы у нее была следующая команда docker:

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

, тогда она работает. (Хотя он использует : последний вместо моей версии (очевидно)).

Так что может быть что-то с тем, как они взаимодействуют.

В свете этого , Он попробовал это:

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

и это:

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

Но ни сработало.

Ответы [ 2 ]

2 голосов
/ 10 января 2020

Расширение переменной и выражения свойства

Анализатор 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 запускает команды через собственный синтаксический анализатор перед передачей аргументов во внешние программы, например, так, чтобы он мог выполнить подстановку переменных et c. На высоком уровне мы можем увидеть, как 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.

By Double строка в кавычках + оператор подвыражения

$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 строковый токен. У него есть вложенные выражения, но он анализируется как один токен и, следовательно, будет возвращен как один аргумент.

0 голосов
/ 09 января 2020

Попробуйте --tag $($imageInfo.FullImageName)`:$version Это должно сказать PowerShell, чтобы оценить это выражение перед передачей его docker.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...