Сценарии Powershell: рекомендуемый способ реализовать ShouldProcess, когда вызовы функции вкладываются?

Сценарий тестирования:

function outer
{
    [cmdletbinding(supportsshouldprocess=$true)]
    param($s)

    process
    {        
        $pscmdlet.shouldprocess("outer $s", "ShouldProcess") | out-null
        "" | out-file "outer $s"

        inner ImplicitPassthru
        inner VerbosePassthru -Verbose:$Verbose 
        inner WhatifPassthru -WhatIf:$WhatIf
    }
}

function inner
{
    [cmdletbinding(supportsshouldprocess=$true)]
    param($s)

    process
    {   
        $pscmdlet.shouldprocess("inner $s", "ShouldProcess") | out-null
        "" | out-file "inner $s"
    }
}

"`n** NORMAL **"
outer normal
"`n** VERBOSE **"
outer verbose -Verbose
"`n** WHATIF **"
outer whatif -WhatIf

Вывод:

** NORMAL **
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

** VERBOSE **
VERBOSE: Performing operation "ShouldProcess" on Target "outer verbose".
VERBOSE: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

** WHATIF **
What if: Performing operation "ShouldProcess" on Target "outer whatif".
What if: Performing operation "Output to File" on Target "outer whatif".
What if: Performing operation "ShouldProcess" on Target "inner ImplicitPassthru".
What if: Performing operation "Output to File" on Target "inner ImplicitPassthru".
What if: Performing operation "ShouldProcess" on Target "inner VerbosePassthru".
What if: Performing operation "Output to File" on Target "inner VerbosePassthru".
What if: Performing operation "ShouldProcess" on Target "inner WhatifPassthru".
What if: Performing operation "Output to File" on Target "inner WhatifPassthru".

К моему глазу здесь существует несколько причуд:

  • Определение-WhatIf: $foo будет всегда включать $WhatIf в вызываемом (и его вызываемые), каков $foo.
  • При определении-WhatIf "для реального" (не ограничивая его к существующей переменной), это распространяет вызываемым неявно. Никакая потребность в passthru или splatting.
  • В отличие от-WhatIf, явного - Подробный, не располагается каскадом вызываемым неявно.
  • Когда Вы пытаетесь вручную passthru - Подробный: $foo, Вы действительно видите, что поведение подобно-WhatIf: $foo. Но это только влияет на сценарии, это вручную тестирует $psCmdlet. ShouldProcess () - созданный в cmdlets не затронуты.

N.B.: Подтвердите ведет себя идентичный WhatIf. Я опустил его для краткости.

Ища сеть и Подключение, я вижу едва любое всестороннее обсуждение поведения ShouldProcess (про или обманный), как принадлежит усовершенствованным функциям. Самой близкой вещью является сообщение от James O'Neill, который рекомендует передать единственный экземпляр $psCmdlet всюду по стеку вызовов. Однако он делает так к обходному решению совершенно другую проблему (избегающий, чтобы несколько - Подтвердили подсказки). Между тем, когда Вы придерживаетесь стандартного $psCmdlet, предоставленного каждой функции, я не вижу документов о том, что ожидать... намного меньше шаблонов разработки, лучших практик, и т.д...

12
задан Richard Berg 17 February 2010 в 07:01
поделиться

1 ответ

Вы не можете ссылаться на $ WhatIf или $ Verbose, поскольку они синтезируются для вас, т.е. этих переменных не существует в вашей функции. Если пользователь указывает их, вы можете получить к ним доступ через $ PSBoundParameters, но если пользователь не указал, то, очевидно, их не будет в этой хеш-таблице.

Когда вы передаете значение переключателю, PowerShell выполнит типичный процесс принуждения, чтобы попытаться преобразовать указанное значение в логическое значение. Поскольку $ whatif не определен, это преобразуется в $ null, что приводит к установке значения переключателя в $ true. Вероятно, это связано с тем, что он видит, что переключатель явно указан без значения, что эквивалентно простому указанию -Whatif без значения. Вы можете увидеть это, отслеживая привязку параметров:

function Foo
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $PSBoundParameters
    }
}

Trace-Command -name ParameterBinding -expr {Foo -whatif:$xyzzy} -PSHost
DEBUG: BIND NAMED cmd line args [Foo]
DEBUG:   BIND arg [] to parameter [WhatIf]
DEBUG:     COERCE arg to [System.Management.Automation.SwitchParameter]
DEBUG:       Arg is null or not present, type is SWITCHPARAMTER, value is true.
DEBUG:         BIND arg [True] to param [WhatIf] SUCCESSFUL
DEBUG: BIND POSITIONAL cmd line args [Foo]
DEBUG: MANDATORY PARAMETER CHECK on cmdlet [Foo]
DEBUG: CALLING BeginProcessing
DEBUG: CALLING EndProcessing

$ WhatIfPreference и $ VerbosePreference устанавливаются должным образом во external в зависимости от того, был ли внешний вызов вызван с помощью -verbose или -whatif. Я вижу, что эти значения прекрасно распространяются на внутреннюю часть. Казалось бы, в $ pscmdlet.ShouldProcess есть ошибка PowerShell.В этом случае, похоже, не соблюдается значение $ VerbosePreference. Вы можете попробовать передать -Verbose во внутренний, например так:

inner VerbosePassthru -Verbose:($VerbosePreference -eq 'Continue')

Другой вариант - использовать Get-Variable -Scope следующим образом:

function Outer
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $pscmdlet.ShouldProcess("Outer process", '') > $null
        inner
        #inner -Verbose:($VerbosePreference -eq 'Continue')
    }
}

function Inner
{
    [CmdletBinding(SupportsShouldProcess=1)]
    param()

    Process
    {
        $pscmdlet = (Get-Variable -Scope 1 -Name PSCmdlet).Value
        $pscmdlet.ShouldProcess("Inner process", '') > $null
        "Inner $VerbosePreference"
    }
}

Outer -Verbose

Я не уверен, что мне это нравится, потому что это подразумевает, что вы знаете, что external на 1 уровень выше внутренний. Вы можете «пройтись» по стеку области видимости в поисках следующей переменной PSCmdlet вверх по стеку. Это эффективно избавляет от необходимости передавать PSCmdlet (что является грубым), но это все еще взлом. Вам следует подумать о том, чтобы сообщить об этом в MS Connect.

11
ответ дан 2 December 2019 в 22:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: