PowerShell Scripting: طريقة موصى بها لتنفيذ يجب أن تكون معالجة المكالمات المتداخلة؟

StackOverflow https://stackoverflow.com/questions/2278876

سؤال

اختبار كتابي:

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".

في عيني هناك العديد من الشذوذ هنا:

  • تحديد -ما هو: $ foo سوف دائماً قم بتشغيل $ Whatif في Callee (و Callees) ، بغض النظر عن $ foo.
  • عندما تحدد -ما هو "حقيقي" (دون تقييده على متغير موجود) ، فإنه ينتشر إلى Callees ضمنيًا. لا حاجة ل passhru أو splatting.
  • على عكس -ما هو ، لا يتسلل verbose الصريح إلى Callees ضمنيًا.
  • عندما تحاول المرور يدويًا -verbose: $ foo ، فإنك ترى أن السلوك يشبه إلى حد ما: $ foo. لكنه يؤثر فقط على البرامج النصية التي تختبر يدويًا pscmdlet.shouldProcess () - لا تتأثر في cmdlets.

NB: تأكيد يتصرف متطابق مع Whatif. لقد حذفته للإيجاز.

البحث عن الويب والاتصال ، أرى بالكاد أي مناقشة متعمقة لسلوك المعالجة (PRO أو CON) يتعلق بالوظائف المتقدمة. أقرب شيء منشور من جيمس أونيل هذا يوصي تمرير مثيل واحد من $ pscmdlet خلال مكدس المكالمات. ومع ذلك ، فإنه يفعل ذلك للتغلب على مشكلة مختلفة تمامًا (تجنب المطالبات المتعددة -التكوين). وفي الوقت نفسه ، عندما تلتزم مع $ pscmdlet القياسية المقدمة لكل وظيفة ، لا أرى مستندات حول ما يمكن توقعه ... أنماط تصميم أقل بكثير ، أفضل الممارسات ، إلخ ...

هل كانت مفيدة؟

المحلول

لا يمكنك حقًا الرجوع إلى $ whatif أو $ inprobose لأنها يتم تصنيعها لك ، أي أن هذه المتغيرات غير موجودة في وظيفتك. إذا قام المستخدم بتحديدها ، فيمكنك الحصول عليها عبر $ psboundparameters ولكن إذا لم يحدد المستخدم ، فمن الواضح أنه لن يكون في هذا الهاش.

عندما تقوم بتمرير قيمة إلى التبديل ، ستقوم PowerShell بإجراء عملية الإكراه النموذجية لمحاولة تحويل القيمة المحددة إلى منطقة. نظرًا لأن $ WhatIF لم يتم تعريف هذا EVALS إلى $ null مما يؤدي إلى ضبط قيمة التبديل على $ true. هذا يفترض لأنه يرى أن المفتاح محدد بشكل صريح مع عدم وجود قيمة فعالة وهو ما يعادل فقط تحديد -ما هو مع عدم وجود قيمة. يمكنك رؤية هذا عند تتبع الربط المعلمة:

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 بشكل مناسب في الخارج بناءً على ما إذا كان تم استدعاء الخارجي مع verbose أو -hatif. أستطيع أن أرى أن هذه القيم تنتشر إلى الداخلية على ما يرام. يبدو أن هناك حشرة PowerShell مع $ pscmdlet.ShouldProcess. لا يبدو أنه يكرم قيمة $ 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

لست متأكدًا من أنني أحب هذا لأنه يعني أنك تعرف أن الخارجي هو مستوى واحد فوق الداخلية. يمكنك "المشي" مكدس النطاق الذي يبحث عن متغير PSCMDLT التالي. يتخلص هذا بشكل فعال من الاضطرار إلى المرور في PSCMDLT (وهو الإجمالي) لكنه لا يزال اختراقًا. يجب أن تفكر في تقديم خطأ على MS Connect حول هذا.

نصائح أخرى

كنت أتطلع إلى كتابة نفس السؤال بالضبط ، وأنا أكتب هذا بعد 7 سنوات تقريبًا. أنا مندهش من أن فريق PowerShell من Microsoft لم يحقق هذا بعد. لقد استنسخت المشكلة في معاينة PowerShell الإصدار 6 (أحدث إصدار).

لقد توصلت إلى حل بديل بسيط ، أي داخل Inner الوظيفة ، نقوم بإنشاء وندير أ scriptblock, ، وضع -Verbose العلم عن طريق التحقق $VerbosePreference الذي تم تعيينه بشكل صحيح على Continue, ، على الرغم من أنه لا يحظى باحترام من قبل ShouldProcess:


Function Outer {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]
    param([string]$Name)

    Process {
        Write-Host "Outer called";
        Inner $Name
    }
}

Function Inner {
    [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]
    param([string]$Name)

    Process {
        if (-not ($PSBoundParameters.ContainsKey('Verbose'))) {
            $PSBoundParameters.Add('Verbose', [bool]$VerbosePreference -eq 'Continue');
        }

        & {
            [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="Medium")]

            param([string]$Name)

            if ($PSCmdlet.ShouldProcess($Name, "Inner")) {
                Write-Host "Inner called";
            }
        } @PSBoundParameters;
    }
}

Export-ModuleMember *
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top