Alles wat u wilde weten over ShouldProcess
PowerShell-functies hebben verschillende functies die de manier waarop gebruikers met hen communiceren aanzienlijk verbeteren.
Een belangrijke functie die vaak over het hoofd wordt gezien, is -WhatIf en -Confirm ondersteuning en het is eenvoudig om aan uw functies toe te voegen. In dit artikel gaan we dieper in op het implementeren van deze functie.
Notitie
De oorspronkelijke versie van dit artikel verscheen op de blog geschreven door @KevinMarquette. Het PowerShell-team bedankt Kevin voor het delen van deze inhoud met ons. Bekijk zijn blog op PowerShellExplained.com.
Dit is een eenvoudige functie die u kunt inschakelen in uw functies om een veiligheidsnet te bieden voor de gebruikers die deze nodig hebben. Er is niets enger dan het uitvoeren van een opdracht die u weet, voor het eerst gevaarlijk kan zijn. De optie om het uit te voeren, -WhatIf kan een groot verschil maken.
CommonParameters
Voordat we kijken naar het implementeren van deze algemene parameters, wil ik even kijken hoe ze worden gebruikt.
-WhatIf gebruiken
Wanneer een opdracht de -WhatIf parameter ondersteunt, kunt u zien wat de opdracht zou hebben gedaan in plaats van wijzigingen aan te brengen. Het is een goede manier om de impact van een opdracht te testen, vooral voordat u iets destructiefs doet.
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".
Als de opdracht correct wordt ShouldProcessgeïmplementeerd, worden alle wijzigingen weergegeven die de opdracht zou hebben aangebracht. Hier volgt een voorbeeld met een jokerteken om meerdere bestanden te verwijderen.
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".
-Confirm gebruiken
Opdrachten die ook ondersteuning bieden -WhatIf-Confirmvoor . Dit geeft u een kans om een actie te bevestigen voordat u deze uitvoert.
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"):
In dit geval hebt u meerdere opties waarmee u kunt doorgaan, een wijziging kunt overslaan of het script kunt stoppen. In de helpprompt wordt elk van deze opties als deze beschreven.
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"):
Lokalisatie
Deze prompt is gelokaliseerd in PowerShell, zodat de taal wordt gewijzigd op basis van de taal van uw besturingssysteem. Dit is nog een ding dat PowerShell voor u beheert.
Schakelen tussen parameters
Laten we even kijken naar manieren om een waarde door te geven aan een switchparameter. De belangrijkste reden waarom ik dit aanroep, is dat u vaak parameterwaarden wilt doorgeven aan functies die u aanroept.
De eerste benadering is een specifieke parametersyntaxis die kan worden gebruikt voor alle parameters, maar u ziet deze meestal gebruikt voor switchparameters. U geeft een dubbele punt op om een waarde aan de parameter te koppelen.
Remove-Item -Path:* -WhatIf:$true
U kunt hetzelfde doen met een variabele.
$DoWhatIf = $true
Remove-Item -Path * -WhatIf:$DoWhatIf
De tweede methode is het gebruik van een hashtabel om de waarde te splateren.
$RemoveSplat = @{
Path = '*'
WhatIf = $true
}
Remove-Item @RemoveSplat
Als u niet bekend bent met hashtables of splatting, heb ik nog een artikel over alles wat u wilt weten over hashtables.
OndersteuntShouldProcess
De eerste stap voor het inschakelen -WhatIf en -Confirm ondersteunen is om op te geven SupportsShouldProcess in de CmdletBinding functie.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
Remove-Item .\myfile1.txt
}
Door op deze manier op te SupportsShouldProcess geven, kunnen we nu onze functie aanroepen met -WhatIf (of -Confirm).
PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Remove File" on target "C:\Temp\myfile1.txt".
U ziet dat ik geen parameter heb gemaakt met de naam -WhatIf. Als u SupportsShouldProcess het opgeeft, wordt deze automatisch voor ons gemaakt. Wanneer we de -WhatIf parameter Test-ShouldProcessopgeven, voeren sommige dingen die we aanroepen ook verwerking uit -WhatIf .
Vertrouwen, maar verifiëren
Er is een gevaar dat alles wat u aanroept -WhatIf , waarden overkrijgt. Voor de rest van de voorbeelden ga ik ervan uit dat het niet werkt en zeer expliciet is bij het aanroepen van andere opdrachten. Ik raad je aan hetzelfde te doen.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
Remove-Item .\myfile1.txt -WhatIf:$WhatIfPreference
}
Ik zal de nuances veel later opnieuw bekijken zodra je een beter begrip hebt van alle stukken in het spel.
$PSCmdlet.ShouldProcess
De methode waarmee u kunt implementeren SupportsShouldProcess , is $PSCmdlet.ShouldProcess. U roept $PSCmdlet.ShouldProcess(...) aan om te zien of u logica moet verwerken en PowerShell zorgt voor de rest. Laten we beginnen met een voorbeeld:
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
$file = Get-ChildItem './myfile1.txt'
if($PSCmdlet.ShouldProcess($file.Name)){
$file.Delete()
}
}
De aanroep om te $PSCmdlet.ShouldProcess($file.name) controleren op de -WhatIf (en -Confirm parameter) verwerkt deze vervolgens dienovereenkomstig. De -WhatIf oorzaken ShouldProcess van het uitvoeren van een beschrijving van de wijziging en retourneert $false:
PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Met een -Confirm aanroep wordt het script onderbroken en wordt de gebruiker gevraagd om door te gaan. Deze retourneert $true als de gebruiker is geselecteerd 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"):
Een geweldige functie van $PSCmdlet.ShouldProcess is dat het verdubbelt als uitgebreide uitvoer. Ik ben hier vaak afhankelijk van bij de implementatie ShouldProcess.
PS> Test-ShouldProcess -Verbose
VERBOSE: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Overloads
Er zijn enkele verschillende overbelastingen voor $PSCmdlet.ShouldProcess met verschillende parameters voor het aanpassen van de berichten. We hebben de eerste al gezien in het bovenstaande voorbeeld. Laten we het eens nader bekijken.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
if($PSCmdlet.ShouldProcess('TARGET')){
# ...
}
}
Dit produceert uitvoer met zowel de functienaam als het doel (waarde van de parameter).
What if: Performing the operation "Test-ShouldProcess" on target "TARGET".
Als u een tweede parameter opgeeft als de bewerking de bewerkingswaarde gebruikt in plaats van de functienaam in het bericht.
## $PSCmdlet.ShouldProcess('TARGET','OPERATION')
What if: Performing the operation "OPERATION" on target "TARGET".
De volgende optie is om drie parameters op te geven om het bericht volledig aan te passen. Wanneer er drie parameters worden gebruikt, is de eerste het hele bericht. De tweede twee parameters worden nog steeds gebruikt in de -Confirm berichtuitvoer.
## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE
Snelzoekgids voor parameters
Voor het geval u hier alleen bent om erachter te komen welke parameters u moet gebruiken, vindt u hier een beknopt overzicht waarin wordt getoond hoe de parameters het bericht in de verschillende -WhatIf scenario's wijzigen.
## $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
Ik gebruik de ene met twee parameters.
ShouldProcessReason
We hebben een vierde overbelasting die geavanceerder is dan de andere. Hiermee kunt u de reden ShouldProcess krijgen dat is uitgevoerd. Ik voeg dit hier alleen toe voor volledigheid, omdat we in plaats daarvan kunnen controleren of $WhatIfPreference dat is $true .
$reason = ''
if($PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION',[ref]$reason)){
Write-Output "Some Action"
}
$reason
We moeten de $reason variabele doorgeven aan de vierde parameter als referentievariabele met [ref]. ShouldProcess vult $reason de waarde None of WhatIf. Ik zei niet dat dit nuttig was en ik had geen reden om het ooit te gebruiken.
Waar u het kunt plaatsen
U gebruikt ShouldProcess dit om uw scripts veiliger te maken. U gebruikt deze dus wanneer uw scripts wijzigingen aanbrengen. Ik wil het $PSCmdlet.ShouldProcess gesprek zo dicht mogelijk bij de verandering plaatsen.
## general logic and variable work
if ($PSCmdlet.ShouldProcess('TARGET','OPERATION')){
# Change goes here
}
Als ik een verzameling items verwerkt, noem ik het voor elk item. Dus de oproep wordt binnen de foreach-lus geplaatst.
foreach ($node in $collection){
# general logic and variable work
if ($PSCmdlet.ShouldProcess($node,'OPERATION')){
# Change goes here
}
}
De reden waarom ik nauw rond de wijziging plaats ShouldProcess , is dat ik zoveel mogelijk code wil uitvoeren wanneer -WhatIf wordt opgegeven. Ik wil dat de installatie en validatie zo mogelijk worden uitgevoerd, zodat de gebruiker deze fouten kan zien.
Ik wil dit ook gebruiken in mijn Pester-tests die mijn projecten valideren. Als ik een stukje logica heb dat moeilijk te bespotten is in pester, kan ik het ShouldProcess vaak verpakken en aanroepen -WhatIf in mijn tests. Het is beter om een deel van uw code te testen dan geen van de code.
$WhatIfPreference
De eerste voorkeursvariabele die we hebben, is $WhatIfPreference. Dit is $false standaard. Als u deze instelt op $true dan wordt uw functie uitgevoerd alsof u hebt opgegeven -WhatIf. Als u dit instelt in uw sessie, worden alle opdrachten uitgevoerd -WhatIf .
Wanneer u een functie aanroept met -WhatIf, wordt de waarde ingesteld $WhatIfPreference op $true binnen het bereik van uw functie.
ConfirmImpact
De meeste van mijn voorbeelden zijn voor -WhatIf , maar alles tot nu toe werkt ook om -Confirm de gebruiker te vragen. U kunt de ConfirmImpact functie instellen op hoog en vraagt de gebruiker alsof deze is aangeroepen.-Confirm
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param()
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
}
Deze aanroep om de -Confirm actie uit te Test-ShouldProcess voeren vanwege de High impact.
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
Het voor de hand liggende probleem is dat het nu moeilijker is om te gebruiken in andere scripts zonder de gebruiker te vragen. In dit geval kunnen we een $false opdracht doorgeven om de prompt te -Confirm onderdrukken.
PS> Test-ShouldProcess -Confirm:$false
Some Action
Ik zal bespreken hoe ik ondersteuning toevoeg -Force in een latere sectie.
$ConfirmPreference
$ConfirmPreference is een automatische variabele die bepaalt wanneer ConfirmImpact u wordt gevraagd de uitvoering te bevestigen. Hier volgen de mogelijke waarden voor zowel als $ConfirmPreferenceConfirmImpact.
HighMediumLowNone
Met deze waarden kunt u verschillende impactniveaus voor elke functie opgeven. Als u een waarde hoger ConfirmImpactdan hebt $ConfirmPreference ingesteld, wordt u niet gevraagd de uitvoering te bevestigen.
Is standaard $ConfirmPreference ingesteld op High en ConfirmImpact is Medium. Als u wilt dat uw functie de gebruiker automatisch vraagt, stelt u uw ConfirmImpact in op High. Stel deze Medium anders in op als het destructief is en wordt gebruikt Low als de opdracht altijd veilig wordt uitgevoerd in productie. Als u deze noneoptie instelt op , wordt er niet gevraagd, zelfs niet als -Confirm deze is opgegeven (maar u -WhatIf krijgt nog steeds ondersteuning).
Wanneer u een functie aanroept, -Confirmwordt de waarde ingesteld $ConfirmPreference op Low binnen het bereik van uw functie.
Geneste bevestigingsprompts onderdrukken
De $ConfirmPreference functie kan worden opgehaald door functies die u aanroept. Dit kan scenario's maken waarbij u een bevestigingsprompt toevoegt en de functie die u aanroept, de gebruiker ook vraagt.
Wat ik meestal doe, is opgeven -Confirm:$false voor de opdrachten die ik aanroep wanneer ik de prompt al heb afgehandeld.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
$file = Get-ChildItem './myfile1.txt'
if($PSCmdlet.ShouldProcess($file.Name)){
Remove-Item -Path $file.FullName -Confirm:$false
}
}
Dit brengt ons terug naar een eerdere waarschuwing: er zijn nuances over wanneer -WhatIf niet wordt doorgegeven aan een functie en wanneer -Confirm deze wordt doorgegeven aan een functie. Ik beloof dat ik hier later weer bij kom.
$PSCmdlet.ShouldContinue
Als u meer controle nodig hebt dan ShouldProcess biedt, kunt u de prompt rechtstreeks activeren met ShouldContinue. ShouldContinue negeert $ConfirmPreference, ConfirmImpact, -Confirm, $WhatIfPreferenceen -WhatIf omdat deze elke keer wordt gevraagd wanneer deze wordt uitgevoerd.
In een oogopslag is het gemakkelijk te verwarren ShouldProcess en ShouldContinue. Ik denk er meestal aan om te gebruiken ShouldProcess omdat de parameter wordt aangeroepen SupportsShouldProcess in de CmdletBinding.
U moet in bijna elk scenario gebruiken ShouldProcess . Daarom heb ik die methode eerst behandeld.
Laten we eens kijken ShouldContinue naar actie.
function Test-ShouldContinue {
[CmdletBinding()]
param()
if($PSCmdlet.ShouldContinue('TARGET','OPERATION')){
Write-Output "Some Action"
}
}
Dit biedt ons een eenvoudigere prompt met minder opties.
Test-ShouldContinue
Second
TARGET
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
Het grootste probleem is ShouldContinue dat de gebruiker deze interactief moet uitvoeren, omdat de gebruiker hier altijd om wordt gevraagd. U moet altijd hulpprogramma's bouwen die kunnen worden gebruikt door andere scripts. De manier waarop u dit doet, is door te implementeren -Force. Ik ga later naar dit idee.
Ja voor iedereen
Dit wordt automatisch afgehandeld, ShouldProcess maar we moeten wat meer werk voor ShouldContinuedoen. Er is een tweede overbelasting van de methode waarbij we een paar waarden moeten doorgeven door te verwijzen naar het beheren van de logica.
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]"
}
}
}
Ik heb een foreach lus en een verzameling toegevoegd om deze in actie weer te geven. Ik heb de ShouldContinue oproep uit de if verklaring gehaald om het beter leesbaar te maken. Het aanroepen van een methode met vier parameters begint een beetje lelijk te worden, maar ik probeerde het er net zo schoon uit te laten zien als ik kon.
Implementeren -Force
ShouldProcess en ShouldContinue moet op verschillende manieren worden geïmplementeerd -Force . De truc voor deze implementaties is dat ShouldProcess altijd moet worden uitgevoerd, maar ShouldContinue niet moet worden uitgevoerd als -Force is opgegeven.
ShouldProcess -Force
Als u dit ConfirmImpacthighinstelt, wordt het eerste waarmee uw gebruikers het proberen te onderdrukken -Force. Dat is het eerste wat ik doe.
Test-ShouldProcess -Force
Error: Test-ShouldProcess: A parameter cannot be found that matches parameter name 'force'.
Als u zich uit de ConfirmImpact sectie herinnert, moeten ze deze eigenlijk als volgt aanroepen:
Test-ShouldProcess -Confirm:$false
Niet iedereen realiseert zich dat ze dat moeten doen en -Force onderdrukt ShouldContinueniet.
Daarom moeten we implementeren -Force voor de saniteit van onze gebruikers. Bekijk dit volledige voorbeeld hier:
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param(
[Switch]$Force
)
if ($Force){
$ConfirmPreference = 'None'
}
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
}
We voegen onze eigen -Force switch toe als parameter. De -Confirm parameter wordt automatisch toegevoegd wanneer deze wordt gebruikt SupportsShouldProcess in de CmdletBinding.
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param(
[Switch]$Force
)
Focus op de -Force logica hier:
if ($Force){
$ConfirmPreference = 'None'
}
Als de gebruiker opgeeft -Force, willen we de bevestigingsprompt onderdrukken, tenzij ze ook opgeven -Confirm. Hierdoor kan een gebruiker een wijziging afdwingen, maar de wijziging nog steeds bevestigen. Vervolgens stellen $ConfirmPreference we het lokale bereik in. Als u nu de -Force parameter gebruikt, wordt de $ConfirmPreference parameter tijdelijk ingesteld op geen, waardoor u wordt gevraagd om bevestiging uit te schakelen.
if ($Force -or $PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
Als iemand beide -Force aangeeft en -WhatIf, -WhatIf dan moet prioriteit worden. Deze aanpak behoudt -WhatIf de verwerking, omdat ShouldProcess deze altijd wordt uitgevoerd.
Voeg geen controle toe voor de $Force waarde in de if instructie met de ShouldProcess. Dat is een antipatroon voor dit specifieke scenario, ook al zie ik je in de volgende sectie voor ShouldContinue.
ShouldContinue -Force
Dit is de juiste manier om te implementeren -Force met ShouldContinue.
function Test-ShouldContinue {
[CmdletBinding()]
param(
[Switch]$Force
)
if($Force -or $PSCmdlet.ShouldContinue('TARGET','OPERATION')){
Write-Output "Some Action"
}
}
Door de $Force operator links van de -or operator te plaatsen, wordt deze eerst geëvalueerd. Schrijf het op deze manier korte circuits de uitvoering van de if instructie. Als $force dat het is $true, wordt de ShouldContinue opdracht niet uitgevoerd.
PS> Test-ShouldContinue -Force
Some Action
We hoeven ons geen zorgen te maken over -Confirm of -WhatIf in dit scenario, omdat ze niet worden ondersteund door ShouldContinue. Daarom moet het anders worden afgehandeld dan ShouldProcess.
Bereikproblemen
Het gebruik en -WhatIf-Confirm moeten van toepassing zijn op alles binnen uw functies en alles wat ze aanroepen. Ze doen dit door het $true instellen of instellen $ConfirmPreference$WhatIfPreferenceLow op in het lokale bereik van de functie. Wanneer u een andere functie aanroept, worden deze waarden aangeroepen.ShouldProcess
Dit werkt meestal correct. Wanneer u ingebouwde cmdlets of een functie in hetzelfde bereik aanroept, werkt dit. Het werkt ook wanneer u vanuit de console een script of functie aanroept in een scriptmodule.
De ene specifieke plaats waar het niet werkt, is wanneer een script of scriptmodule een functie aanroept in een andere scriptmodule. Dit klinkt misschien niet als een groot probleem, maar de meeste modules die u maakt of ophaalt uit de PSGallery zijn scriptmodules.
Het kernprobleem is dat scriptmodules de waarden voor $WhatIfPreference of $ConfirmPreference (en meerdere andere) niet overnemen wanneer ze worden aangeroepen vanuit functies in andere scriptmodules.
De beste manier om dit als algemene regel samen te vatten, is dat dit correct werkt voor binaire modules en nooit vertrouwt op het werken voor scriptmodules. Als u het niet zeker weet, test u deze of gaat u ervan uit dat deze niet goed werkt.
Persoonlijk vind ik dit erg gevaarlijk omdat het scenario's creëert waarin u ondersteuning toevoegt -WhatIf aan meerdere modules die correct in isolatie werken, maar niet correct werken wanneer ze elkaar bellen.
We hebben wel een GitHub RFC die werkt om dit probleem op te lossen. Zie Uitvoeringsvoorkeuren doorgeven buiten het bereik van de scriptmodule voor meer informatie.
Bij sluiten
Ik moet opzoeken hoe ik het moet gebruiken ShouldProcess elke keer dat ik het moet gebruiken. Het duurde lang voordat ik onderscheid ShouldProcess maakte tussen ShouldContinue. Ik moet bijna altijd opzoeken welke parameters moeten worden gebruikt. Dus maak je geen zorgen als je van tijd tot tijd nog steeds in de war raakt. Dit artikel is hier wanneer u het nodig hebt. Ik weet zeker dat ik er zelf vaak naar zal verwijzen.
Als u dit bericht leuk vond, kunt u uw gedachten met mij delen op Twitter via de onderstaande link. Ik hoor altijd graag van mensen die waarde krijgen uit mijn inhoud.