Minden, amit tudni akart a ShouldProcess-ről

A PowerShell-függvények számos olyan funkcióval rendelkeznek, amelyek jelentősen javítják a felhasználók interakcióját. Az egyik fontos funkció, amelyet gyakran figyelmen kívül hagyunk, az a -WhatIf-Confirm támogatás, és könnyen hozzáadható a függvényekhez. Ebben a cikkben részletesen bemutatjuk, hogyan valósítható meg ez a funkció.

Feljegyzés

A cikk eredeti verziója @KevinMarquette által írt blogon jelent meg. A PowerShell csapata köszönjük Kevinnek, hogy megosztotta velünk ezt a tartalmat. Kérjük, nézze meg a blogját a PowerShellExplained.com.

Ez egy egyszerű funkció, amely lehetővé teszi a funkciókban, hogy biztonsági hálót biztosítson a szükséges felhasználóknak. Semmi sem rémisztőbb, mint egy olyan parancs futtatása, amelyről tudja, hogy első alkalommal veszélyes lehet. A futtatás -WhatIf lehetősége nagy különbséget tehet.

CommonParameters

Mielőtt megvizsgáljuk ezeknek a gyakori paramétereknek a implementálását, szeretném gyorsan áttekinteni a használatuk módját.

A -WhatIf használata

Ha egy parancs támogatja a -WhatIf paramétert, láthatja, hogy a parancs mit tett volna ahelyett, hogy módosításokat végezne. Ez egy jó módszer a parancsok hatásának tesztelésére, különösen mielőtt valami romboló műveletet végez.

PS C:\temp> Get-ChildItem
    Directory: C:\temp
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         4/19/2021   8:59 AM              0 importantfile.txt
-a----         4/19/2021   8:58 AM              0 myfile1.txt
-a----         4/19/2021   8:59 AM              0 myfile2.txt

PS C:\temp> Remove-Item -Path .\myfile1.txt -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".

Ha a parancs megfelelően implementál ShouldProcess, az összes módosítást meg kell jelenítenie. Íme egy példa, amely egy helyettesítő karaktert használ több fájl törléséhez.

PS C:\temp> Remove-Item -Path * -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
What if: Performing the operation "Remove File" on target "C:\Temp\myfile2.txt".
What if: Performing the operation "Remove File" on target "C:\Temp\importantfile.txt".

A -Confirm használata

A támogatott -WhatIf parancsok szintén támogatják -Confirma parancsokat. Ez lehetőséget ad egy művelet megerősítésére a művelet végrehajtása előtt.

PS C:\temp> Remove-Item .\myfile1.txt -Confirm

Confirm
Are you sure you want to perform this action?
Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Ebben az esetben több lehetőség közül választhat, amelyek lehetővé teszik a folytatást, a módosítás kihagyását vagy a szkript leállítását. A súgó az ilyen lehetőségek mindegyikét ismerteti.

Y - Continue with only the next step of the operation.
A - Continue with all the steps of the operation.
N - Skip this operation and proceed with the next operation.
L - Skip this operation and all subsequent operations.
S - Pause the current pipeline and return to the command prompt. Type "exit" to resume the pipeline.
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Honosítás

Ez a kérés honosított a PowerShellben, így a nyelv az operációs rendszer nyelvétől függően változik. Ez még egy dolog, amit a PowerShell kezel.

Paraméterek váltása

Vegyünk egy gyors pillanatot, hogy megnézzük, hogyan adhatunk át értéket egy kapcsolóparaméternek. Ennek fő oka az, hogy gyakran szeretne paraméterértékeket átadni a hívott függvények számára.

Az első megközelítés egy adott paraméterszintaxis, amely minden paraméterhez használható, de többnyire a kapcsolóparaméterekhez használatos. Meg kell adnia egy kettőspontot, amely egy értéket csatol a paraméterhez.

Remove-Item -Path:* -WhatIf:$true

Ugyanezt megteheti egy változóval is.

$DoWhatIf = $true
Remove-Item -Path * -WhatIf:$DoWhatIf

A második módszer egy kivonatoló használata az érték splatására.

$RemoveSplat = @{
    Path = '*'
    WhatIf = $true
}
Remove-Item @RemoveSplat

Ha még nem ismeri a kivonatolókat vagy a platformokat, van egy másik cikkem, amely mindenről szól , amit tudni szeretne a kivonatolókról.

Támogatja a ShouldProcess-et

Az engedélyezés -WhatIf és -Confirm támogatás első lépése a függvényben CmdletBinding való megadásaSupportsShouldProcess.

function Test-ShouldProcess {
    [CmdletBinding(SupportsShouldProcess)]
    param()
    Remove-Item .\myfile1.txt
}

Ha így adja meg SupportsShouldProcess a függvényt, meghívhatjuk a függvényt -WhatIf (vagy -Confirm).

PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".

Figyelje meg, hogy nem hoztam létre egy .-WhatIf A beállítás automatikusan SupportsShouldProcess létrehozza számunkra. Amikor megadjuk a -WhatIf paramétert, Test-ShouldProcessbizonyos dolgok, amelyeket nevezünk, feldolgozást is végrehajtanak -WhatIf .

Megbízhatóság, de ellenőrzés

Fennáll a veszélye annak, hogy minden, amit nevez, örökli az értékeket -WhatIf . A többi példa esetében feltételezem, hogy ez nem működik, és nagyon explicit, amikor más parancsokat hív meg. Azt javaslom, hogy ugyanezt tegye.

function Test-ShouldProcess {
    [CmdletBinding(SupportsShouldProcess)]
    param()
    Remove-Item .\myfile1.txt -WhatIf:$WhatIfPreference
}

Sokkal később újra megismétlem az árnyalatokat, ha jobban megérted a darabokat.

$PSCmdlet.ShouldProcess

A implementálást SupportsShouldProcess lehetővé tevő módszer az $PSCmdlet.ShouldProcess. Felhívhatja $PSCmdlet.ShouldProcess(...) , hogy fel kell-e dolgoznia néhány logikát, és a PowerShell gondoskodik a többiről. Kezdjük egy példával:

function Test-ShouldProcess {
    [CmdletBinding(SupportsShouldProcess)]
    param()

    $file = Get-ChildItem './myfile1.txt'
    if($PSCmdlet.ShouldProcess($file.Name)){
        $file.Delete()
    }
}

A hívás ellenőrzi $PSCmdlet.ShouldProcess($file.name) a (és -Confirm paraméter -WhatIf ) értékét, majd ennek megfelelően kezeli azt. A -WhatIf változás leírásának és visszatérésének $falseokaiShouldProcess:

PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".

A szkriptet használó -Confirm hívás szünetelteti a szkriptet, és felkéri a felhasználót a folytatásra. Akkor adja vissza, $true ha a felhasználó ki van választva Y.

PS> Test-ShouldProcess -Confirm
Confirm
Are you sure you want to perform this action?
Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Ennek egyik nagyszerű funkciója $PSCmdlet.ShouldProcess , hogy részletes kimenetként duplázódik. Én függ ez gyakran, amikor implementálás ShouldProcess.

PS> Test-ShouldProcess -Verbose
VERBOSE: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".

Túlterhelések

Az üzenetkezelés testreszabásához $PSCmdlet.ShouldProcess különböző paraméterekkel rendelkezik néhány különböző túlterhelés. A fenti példában már láttuk az elsőt. Nézzük meg közelebbről.

function Test-ShouldProcess {
    [CmdletBinding(SupportsShouldProcess)]
    param()

    if($PSCmdlet.ShouldProcess('TARGET')){
        # ...
    }
}

Ez olyan kimenetet hoz létre, amely tartalmazza a függvény nevét és a célt (a paraméter értékét).

What if: Performing the operation "Test-ShouldProcess" on target "TARGET".

Ha egy második paramétert ad meg, a művelet a művelet értékét használja az üzenetben szereplő függvénynév helyett.

## $PSCmdlet.ShouldProcess('TARGET','OPERATION')
What if: Performing the operation "OPERATION" on target "TARGET".

A következő lehetőség három paraméter megadása az üzenet teljes testreszabásához. Ha három paramétert használ, az első a teljes üzenet. A második két paraméter továbbra is használatos az -Confirm üzenet kimenetében.

## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE

Gyorsparaméter-referencia

Arra az esetre, ha csak azért jött volna ide, hogy megtudja, milyen paramétereket kell használnia, az alábbi rövid útmutató bemutatja, hogy a paraméterek hogyan módosítják az üzenetet a különböző -WhatIf forgatókönyvekben.

## $PSCmdlet.ShouldProcess('TARGET')
What if: Performing the operation "FUNCTION_NAME" on target "TARGET".

## $PSCmdlet.ShouldProcess('TARGET','OPERATION')
What if: Performing the operation "OPERATION" on target "TARGET".

## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE

Az egyiket általában két paraméterrel használom.

ShouldProcessReason

Van egy negyedik túlterhelésünk, ami fejlettebb, mint a többiek. Ez lehetővé teszi, hogy megkapja az ok ShouldProcess végrehajtását. Csak azért adom hozzá ezt a teljesség érdekében, mert csak ellenőrizni $WhatIfPreference tudjuk, hogy ez-e $true .

$reason = ''
if($PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION',[ref]$reason)){
    Write-Output "Some Action"
}
$reason

A változót $reason referenciaváltozóként át kell adnunk a negyedik paraméternek [ref]. ShouldProcess a $reason az értékkel None vagy WhatIfa . Nem mondtam, hogy ez hasznos, és nem volt okom arra, hogy valaha is használhassam.

Hol helyezze el?

ShouldProcess Ezzel biztonságosabbá teheti a szkripteket. Ezért akkor használja, amikor a szkriptek módosításokat végeznek. Szeretem a hívást a $PSCmdlet.ShouldProcess lehető legközelebb helyezni a változáshoz.

## general logic and variable work
if ($PSCmdlet.ShouldProcess('TARGET','OPERATION')){
    # Change goes here
}

Ha egy elemgyűjteményt dolgozok fel, minden elemhez hívom. Így a hívás a foreach hurokba kerül.

foreach ($node in $collection){
    # general logic and variable work
    if ($PSCmdlet.ShouldProcess($node,'OPERATION')){
        # Change goes here
    }
}

Azért helyezem ShouldProcess szigorúan a módosítást, mert a lehető legtöbb kódot szeretném végrehajtani, amikor -WhatIf meg van adva. Ha lehetséges, futtassa a beállítást és az ellenőrzést, hogy a felhasználó láthassa ezeket a hibákat.

Ezt is szeretem használni a Pester-tesztjeimben, amelyek ellenőrzik a projektjeimet. Ha van egy olyan logikám, amelyet nehéz kigúnyolni a kártevőben, gyakran becsomagolhatom ShouldProcess , és meghívhatom a -WhatIf tesztjeimben. Jobb, ha a kód egy részét teszteli, mint egyiket sem.

$WhatIfPreference

Az első beállítási változónk a .$WhatIfPreference Ez alapértelmezés szerint így van $false . Ha úgy állítja be, hogy $true a függvény a megadott -WhatIfmódon fut. Ha ezt a munkamenetben állítja be, az összes parancs végrehajtja a végrehajtást -WhatIf .

Amikor függvényt -WhatIfhív meg, a függvény értéke $WhatIfPreference a függvény hatókörén belülre lesz állítva $true .

ConfirmImpact

A legtöbb példa erre vonatkozik -WhatIf , de az eddigiekben minden a felhasználó kérésére -Confirm is használható. Beállíthatja, hogy a ConfirmImpact függvény magas legyen, és a rendszer úgy kéri a felhasználót, mintha meghívták -Confirmvolna.

function Test-ShouldProcess {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param()

    if ($PSCmdlet.ShouldProcess('TARGET')){
        Write-Output "Some Action"
    }
}

Ez a Test-ShouldProcess hívás a -Confirm hatás miatt hajtja végre a High műveletet.

PS> Test-ShouldProcess

Confirm
Are you sure you want to perform this action?
Performing the operation "Test-ShouldProcess" on target "TARGET".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"): y
Some Action

A nyilvánvaló probléma az, hogy most már nehezebb más szkriptekben használni anélkül, hogy a felhasználót kéri. Ebben az esetben átadhatunk egy parancsot $false a kérés letiltásához -Confirm .

PS> Test-ShouldProcess -Confirm:$false
Some Action

Egy későbbi szakaszban bemutatom, hogyan adhatok hozzá -Force támogatást.

$ConfirmPreference

$ConfirmPreference egy automatikus változó, amely szabályozza, amikor ConfirmImpact a végrehajtás megerősítésére kéri. Íme a lehetséges értékek mind $ConfirmPreference a kettő és ConfirmImpacta .

  • High
  • Medium
  • Low
  • None

Ezekkel az értékekkel különböző hatásszinteket adhat meg az egyes függvényekhez. Ha $ConfirmPreference a beállítás értéke magasabb, mint ConfirmImpact, akkor a rendszer nem kéri a végrehajtás megerősítését.

Alapértelmezés szerint $ConfirmPreference a beállítás értéke High és ConfirmImpact értéke .Medium Ha azt szeretné, hogy a függvény automatikusan kérje a felhasználót, állítsa be a ConfirmImpact következőt High: . Egyéb esetben állítsa be, Medium ha a romboló, és használja Low , ha a parancs mindig biztonságos futtatható éles környezetben. Ha ezt állítja be none, akkor sem kéri, ha -Confirm meg van adva (de továbbra is támogatást nyújt -WhatIf ).

Függvény meghívásakor a függvény -Confirmértéke $ConfirmPreference a függvény hatókörén belülre lesz állítva Low .

Beágyazott megerősítési kérések letiltása

Az $ConfirmPreference ön által hívott függvények átvehetik azokat. Ez olyan forgatókönyveket hozhat létre, amikor megerősítési kérést ad hozzá, és a hívott függvény is kéri a felhasználót.

Általában meg kell adnia -Confirm:$false a parancsokat, amelyeket akkor hívom, amikor már kezeltem a kérést.

function Test-ShouldProcess {
    [CmdletBinding(SupportsShouldProcess)]
    param()

    $file = Get-ChildItem './myfile1.txt'
    if($PSCmdlet.ShouldProcess($file.Name)){
        Remove-Item -Path $file.FullName -Confirm:$false
    }
}

Ezzel visszatérünk egy korábbi figyelmeztetéshez: Vannak árnyalatok, amelyek azt jelzik, hogy mikor -WhatIf nem adhatók át függvényeknek, és mikor -Confirm kerülnek át egy függvényre. Ígérem, hogy később visszatérek ehhez.

$PSCmdlet.ShouldContinue

Ha a megadottnál ShouldProcess több vezérlőre van szüksége, a parancssort közvetlenül a következővel ShouldContinueindíthatja el: . ShouldContinue$ConfirmPreferencefigyelmen kívül hagyja a , ConfirmImpact, , -Confirm$WhatIfPreference, és -WhatIf mert minden alkalommal kéri, amikor végrehajtja.

Egy gyors pillantással könnyen összezavarható ShouldProcess és ShouldContinue. Általában emlékszem, hogy használja ShouldProcess , mert a paraméter meghívja SupportsShouldProcess a CmdletBinding. Szinte minden forgatókönyvben érdemes használni ShouldProcess . Ezért foglalkoztam először ezzel a módszerrel.

Vessünk egy pillantást ShouldContinue működés közben.

function Test-ShouldContinue {
    [CmdletBinding()]
    param()

    if($PSCmdlet.ShouldContinue('TARGET','OPERATION')){
        Write-Output "Some Action"
    }
}

Ez egyszerűbb kérést biztosít, kevesebb lehetőséggel.

Test-ShouldContinue

Second
TARGET
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"):

A legnagyobb probléma ShouldContinue az, hogy a felhasználónak interaktívan kell futtatnia, mert mindig kéri a felhasználót. Mindig olyan eszközöket kell összeállítani, amelyeket más szkriptek is használhatnak. Ennek módja a implementálás -Force. Később újra megnézem ezt az ötletet.

Igen, mindet

Ez automatikusan kezeli, ShouldProcess de meg kell csinálni egy kicsit több munkát ShouldContinue. Van egy második metódus túlterhelése, ahol néhány értéket kell átadnunk a logika szabályozásához.

function Test-ShouldContinue {
    [CmdletBinding()]
    param()

    $collection = 1..5
    $yesToAll = $false
    $noToAll = $false

    foreach($target in $collection) {

        $continue = $PSCmdlet.ShouldContinue(
                "TARGET_$target",
                'OPERATION',
                [ref]$yesToAll,
                [ref]$noToAll
            )

        if ($continue){
            Write-Output "Some Action [$target]"
        }
    }
}

Hozzáadtam egy hurkot foreach és egy gyűjteményt, hogy működés közben megjelenítsem. Kihúztam a ShouldContinue hívást az if utasításból, hogy könnyebben olvasható legyen. A négy paraméterrel rendelkező metódusok meghívása egy kicsit csúnyán kezd eltelni, de megpróbáltam olyan tisztanak tűnni, amennyire csak tudtam.

Implementálás –Force

ShouldProcess és ShouldContinue különböző módokon kell implementálniuk -Force . Ezeknek az implementációknak az a trükkje, hogy ShouldProcess mindig végre kell hajtani, de ShouldContinue ha meg van adva, nem szabad végrehajtani -Force őket.

ShouldProcess –Force

Ha ezt állítja ConfirmImpacthighbe, a felhasználók először megpróbálják letiltani -Force. Amúgy is ez az első dolgom.

Test-ShouldProcess -Force
Error: Test-ShouldProcess: A parameter cannot be found that matches parameter name 'force'.

Ha visszaemlékeznek a ConfirmImpact szakaszból, valójában így kell hívniuk:

Test-ShouldProcess -Confirm:$false

Nem mindenki veszi észre, hogy meg kell tennie, és -Force nem tiltja el ShouldContinue. Ezért a felhasználók józanságát kell megvalósítanunk -Force . Tekintse meg ezt a teljes példát:

function Test-ShouldProcess {
    [CmdletBinding(
        SupportsShouldProcess,
        ConfirmImpact = 'High'
    )]
    param(
        [Switch]$Force
    )

    if ($Force -and -not $Confirm){
        $ConfirmPreference = 'None'
    }

    if ($PSCmdlet.ShouldProcess('TARGET')){
        Write-Output "Some Action"
    }
}

Paraméterként saját -Force kapcsolót adunk hozzá. A -Confirm paraméter automatikusan hozzáadódik a SupportsShouldProcessCmdletBinding.

[CmdletBinding(
    SupportsShouldProcess,
    ConfirmImpact = 'High'
)]
param(
    [Switch]$Force
)

A logikára összpontosítva -Force :

if ($Force -and -not $Confirm){
    $ConfirmPreference = 'None'
}

Ha a felhasználó megadja, el szeretnénk -Forcetiltani a megerősítési kérést, kivéve, ha ők is megadják -Confirm. Ez lehetővé teszi, hogy a felhasználó kényszerítse a módosítást, de továbbra is erősítse meg a módosítást. Ezután a helyi hatókört állítjuk be $ConfirmPreference . Most, a -Force paraméter használatával ideiglenesen nincs értékre állítja a $ConfirmPreference paramétert, letiltva a megerősítési kérést.

if ($PSCmdlet.ShouldProcess('TARGET')){
        Write-Output "Some Action"
    }

Ha valaki mindkettőt -Force-WhatIfmegadja, akkor -WhatIf elsőbbséget kell élveznie. Ez a megközelítés megőrzi a feldolgozást -WhatIf , mert ShouldProcess mindig végrehajtásra kerül.

Ne adja hozzá az érték ellenőrzését az $Force utasításban a ifShouldProcesskövetkezővel: . Ez egy anti-minta erre a konkrét forgatókönyvre annak ellenére, hogy ezt mutatom a következő szakaszban ShouldContinue.

ShouldContinue -Force

Ez a helyes módja annak, hogy implementáljuk -Force a következővel ShouldContinue: .

function Test-ShouldContinue {
    [CmdletBinding()]
    param(
        [Switch]$Force
    )

    if($Force -or $PSCmdlet.ShouldContinue('TARGET','OPERATION')){
        Write-Output "Some Action"
    }
}

Ha az $Force operátor bal oldalán helyezi el az -or értéket, először kiértékeli azt. Ha így ír, rövidre zárhatja az utasítás végrehajtását if . Ha $force igen $true, akkor a ShouldContinue rendszer nem hajtja végre a parancsot.

PS> Test-ShouldContinue -Force
Some Action

Nem kell aggódnia -Confirm , vagy -WhatIf ebben a forgatókönyvben, mert nem támogatja ShouldContinueőket. Ezért kell másképpen kezelni, mint ShouldProcess.

Hatókörrel kapcsolatos problémák

A függvények -WhatIf és -Confirm az általuk hívott függvények minden elemére alkalmazhatók és alkalmazhatók. Ezt úgy $WhatIfPreference hajtják végre, hogy $true a függvény helyi hatókörében a beállításra vagy beállításra Low vannak beállítva$ConfirmPreference. Amikor egy másik függvényt hív meg, az értékek használatára hívja meg a hívást ShouldProcess .

Ez az idő nagy részében helyesen működik. Bármikor működik, amikor meghív egy beépített parancsmagot vagy egy függvényt ugyanabban a hatókörben. Akkor is működik, ha szkriptet vagy függvényt hív meg egy szkriptmodulban a konzolról.

Az egyetlen hely, ahol nem működik, ha egy szkript vagy egy szkriptmodul meghív egy függvényt egy másik szkriptmodulban. Ez nem hangzik nagy problémának, de a PSGalleryből létrehozott vagy lekérhető modulok többsége szkriptmodul.

Az alapvető probléma az, hogy a szkriptmodulok nem öröklik az értékeket $WhatIfPreference más szkriptmodulok függvényeiből, vagy $ConfirmPreference (és több más) is.

Ennek általános szabályként történő összegzésének legjobb módja, ha ez megfelelően működik bináris modulok esetében, és soha nem bízik benne, hogy szkriptmodulok esetén is működni fog. Ha nem biztos benne, tesztelje, vagy csak feltételezze, hogy nem működik megfelelően.

Személy szerint úgy érzem, hogy ez nagyon veszélyes, mert olyan forgatókönyveket hoz létre, ahol támogatást ad -WhatIf több modulhoz, amelyek külön-külön megfelelően működnek, de nem működnek megfelelően, amikor meghívják egymást.

Egy GitHub RFC dolgozik a probléma megoldásán. További részletekért lásd: Végrehajtási beállítások propagálása a szkriptmodul hatókörön túl.

Záró

Meg kell néznem, hogyan kell használni ShouldProcess minden alkalommal, amikor szükségem van rá. Sokáig tartott megkülönböztetni ShouldProcessShouldContinue. Szinte mindig meg kell néznem, hogy milyen paramétereket kell használni. Szóval ne aggódj, ha még mindig összezavarodsz időről időre. Ez a cikk akkor jelenik meg, amikor szüksége lesz rá. Biztos vagyok benne, hogy magam is gyakran hivatkozom rá.

Ha tetszett ez a bejegyzés, kérjük, ossza meg gondolatait velem a Twitter segítségével az alábbi linket. Mindig szeretem hallani azokat az embereket, akik értéket kapnak a tartalomból.