Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
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 het blog dat is 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 in uw functies kunt inschakelen 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 gevaarlijk kan zijn voor de eerste keer. De optie om deze met -WhatIf uit te voeren kan een groot verschil maken.
GemeenschappelijkeParameters
Voordat we deze algemene parameters gaan implementeren, 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 destructief 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 het commando ShouldProcess correct wordt geïmplementeerd, zou het u alle wijzigingen kunnen tonen die het zou kunnen aanbrengen. Hier volgt een voorbeeld met behulp van 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 -WhatIf ondersteunen, ondersteunen ook -Confirm. 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 zoals 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 wordt 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.
[switch] Parameters
Laten we even kijken naar manieren om een waarde door te geven aan een [switch] parameter. 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 [switch] parameters. U specificeert een dubbelpunt om een waarde te koppelen aan de parameter.
Remove-Item -Path:* -WhatIf:$true
U kunt hetzelfde doen met een variabele.
$DoWhatIf = $true
Remove-Item -Path * -WhatIf:$DoWhatIf
De tweede benadering is het gebruik van een hashtabel om de waarde te verspreiden.
$RemoveSplat = @{
Path = '*'
WhatIf = $true
}
Remove-Item @RemoveSplat
Als u niet bekend bent met hashtables of splatting, heb ik een ander artikel daarover dat alles wat u over hashtables wilde weten behandelt.
Ondersteunt ShouldProcess
De eerste stap voor het inschakelen van -WhatIf en -Confirm ondersteuning is om SupportsShouldProcess te specificeren in het CmdletBinding van uw functie.
function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()
Remove-Item .\myfile1.txt
}
Door op deze manier op te geven SupportsShouldProcess , 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 opgeeft, wordt deze automatisch voor ons gemaakt. Wanneer we de parameter -WhatIf op Test-ShouldProcess specificeren, wordt ook de -WhatIf verwerking uitgevoerd.
Notitie
Wanneer u powerShell gebruikt SupportsShouldProcess, wordt de $WhatIf variabele niet toegevoegd aan de functie. U hoeft de waarde $WhatIf ervan niet te controleren omdat de ShouldProcess() methode voor u zorgt.
Vertrouwen maar verifiëren
Er bestaat een gevaar dat alles wat u aanroept -WhatIf waarden over neemt. 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 als 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 naar $PSCmdlet.ShouldProcess($file.Name) controleert -WhatIf (en de -Confirm-parameter) en verwerkt deze dienovereenkomstig. De -WhatIf zorgt ervoor dat ShouldProcess een beschrijving van de wijziging uitvoert en $false retourneert.
PS> Test-ShouldProcess -WhatIf
What if: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Met een oproep wordt -Confirm het script gepauzeerd en krijgt de gebruiker de optie om door te gaan. Deze retourneert $true als de gebruiker Y heeft geselecteerd.
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 ook dienst doet als gedetailleerde uitvoer. Ik ben hier vaak van afhankelijk bij de implementatie ShouldProcess.
PS> Test-ShouldProcess -Verbose
VERBOSE: Performing the operation "Test-ShouldProcess" on target "myfile1.txt".
Overbelastingen
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 die zowel de functienaam als het doel (waarde van de parameter) bevat.
What if: Performing the operation "Test-ShouldProcess" on target "TARGET".
Bij het specificeren van een tweede parameter wordt 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 het eerste het hele bericht. De tweede twee parameters worden nog steeds gebruikt in de -Confirm berichtuitvoer.
## $PSCmdlet.ShouldProcess('MESSAGE','TARGET','OPERATION')
What if: MESSAGE
Snelle parameterreferentie
Als u hier alleen bent gekomen om te achterhalen welke parameters u moet gebruiken, is hier een beknopt overzicht van 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 ophalen waarom ShouldProcess is uitgevoerd. Ik voeg dit hier alleen toe voor volledigheid, omdat we in plaats daarvan kunnen controleren of $WhatIfPreference dat zo 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
$reason wordt gevuld met 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 om uw scripts veiliger te maken. U gebruikt deze dus wanneer uw scripts wijzigingen aanbrengen. Ik wil de $PSCmdlet.ShouldProcess oproep zo dicht mogelijk bij de wijziging plaatsen.
## general logic and variable work
if ($PSCmdlet.ShouldProcess('TARGET','OPERATION')){
# Change goes here
}
Als ik een verzameling van items verwerk, roep ik het aan voor ieder item. Het gesprek wordt dus 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 ShouldProcess nauw rond de wijziging plaats, is dat ik zoveel mogelijk code wil uitvoeren wanneer -WhatIf is 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 mocken is in Pester, kan ik het vaak in ShouldProcess verpakken en met -WhatIf in mijn tests aanroepen. 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 -WhatIf uitgevoerd.
Wanneer u een functie aanroept met -WhatIf, wordt de waarde van $WhatIfPreference binnen het bereik van uw functie ingesteld op $true.
ConfirmImpact
De meeste van mijn voorbeelden zijn voor -WhatIf, maar alles tot nu toe werkt ook met -Confirm om de gebruiker te activeren. U kunt de ConfirmImpact van de functie instellen op hoog en deze geeft de gebruiker een aanmaning alsof deze was aangeroepen met -Confirm.
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param()
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
}
Deze oproep naar Test-ShouldProcess voert de -Confirm actie uit 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 doorgeven naar -Confirm om de prompt te 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 $ConfirmPreference als ConfirmImpact.
HighMediumLowNone
Met deze waarden kunt u verschillende impactniveaus voor elke functie opgeven. Als u $ConfirmPreference hebt ingesteld op een waarde die hoger is dan ConfirmImpact, wordt u niet gevraagd om de uitvoering te bevestigen.
Is standaard $ConfirmPreference ingesteld op High en ConfirmImpact is Medium. Als u wilt dat de functie de gebruiker automatisch vraagt, stelt u uw ConfirmImpact in op High. Stel het in op Medium als het destructief is, en op Low als de opdracht altijd veilig kan worden uitgevoerd in productie. Als u het instelt op none, ontvangt u geen prompt, zelfs niet als -Confirm is opgegeven (maar u krijgt nog steeds -WhatIf-ondersteuning).
Wanneer u een functie aanroept met -Confirm, wordt de waarde van $ConfirmPreference binnen het bereik van uw functie ingesteld op Low.
Geneste bevestigingsprompts onderdrukken
De $ConfirmPreference functie kan worden opgehaald door functies die u aanroept. Dit kan scenario's creëren waarbij u een bevestigingsprompt toevoegt en de functie die u aanroept, de gebruiker ook om bevestiging vraagt.
Wat ik meestal doe, is opgeven -Confirm:$false voor de opdrachten die ik aanroep wanneer ik de prompt al heb verwerkt.
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 er later weer bij kom.
$PSCmdlet.ShouldContinue
Als u meer controle nodig hebt dan ShouldProcess biedt, kunt u de prompt rechtstreeks activeren met ShouldContinue.
ShouldContinuenegeert $ConfirmPreference, ConfirmImpact, , -Confirmen $WhatIfPreference-WhatIf omdat deze elke keer wordt gevraagd wanneer deze wordt uitgevoerd.
In een oogopslag is het gemakkelijk om te verwarren ShouldProcess en ShouldContinue. Ik denk er meestal aan ShouldProcess te gebruiken omdat de parameter SupportsShouldProcess wordt genoemd in de CmdletBinding.
U moet in vrijwel elk scenario gebruiken ShouldProcess . Daarom heb ik die methode eerst behandeld.
Laten we ShouldContinue in actie zien.
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
OPERATION
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 kom later terug naar dit idee.
Ja voor alle
Dit wordt automatisch afgehandeld met ShouldProcess, maar we moeten voor ShouldContinue wat meer werk doen. Er is een tweede methode-overload waarbij we een paar waarden als referentie moeten doorgeven om de logica te beheren.
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 zodat ze in actie kunnen worden getoond. 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 van -Force
ShouldProcess en ShouldContinue moeten -Force op verschillende manieren implementeren. 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 uw ConfirmImpact op high instelt, zullen uw gebruikers als eerste proberen het te onderdrukken met -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 gezondheid van onze gebruikers. Bekijk dit volledige voorbeeld hier:
function Test-ShouldProcess {
[CmdletBinding(
SupportsShouldProcess,
ConfirmImpact = 'High'
)]
param(
[switch]$Force
)
if ($Force -and -not $PSBoundParameters.ContainsKey('Confirm')) {
$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 bij het gebruik van SupportsShouldProcess in de CmdletBinding. Wanneer u echter gebruikt SupportsShouldProcess, voegt PowerShell de $Confirm variabele niet toe aan de functie. Als u in de strikte modus werkt en de $Confirm variabele probeert te gebruiken voordat deze is gedefinieerd, krijgt u een foutmelding. U kunt de fout vermijden door $PSBoundParameters te gebruiken om te testen of de parameter door de gebruiker is doorgegeven.
if ($Force -and -not $PSBoundParameters.ContainsKey('Confirm')) {
$ConfirmPreference = 'None'
}
Als de gebruiker -Force aangeeft, stellen we $ConfirmPreference op None binnen de lokale scope. Als de gebruiker ook -Confirm opgeeft, dan worden de waarden van de -Confirm-parameter door ShoudProcess() gehonoreerd.
if ($PSCmdlet.ShouldProcess('TARGET')){
Write-Output "Some Action"
}
Als iemand zowel -Force als -WhatIf aangeeft, moet -WhatIf voorrang krijgen. Deze benadering behoudt de -WhatIf verwerking, omdat ShouldProcess deze altijd wordt uitgevoerd.
Voeg geen test toe voor de $Force-waarde binnen de if-instructie met de ShouldProcess. Dat is een antipatroon voor dit specifieke scenario, ook al laat ik u in de volgende sectie voor ShouldContinue zien.
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. Op deze manier schrijven voorkomt verdere uitvoering van de if instructie. Als $Force$true is, wordt ShouldContinue 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 -WhatIf en -Confirm moeten van toepassing zijn op alles binnen uw functies en alles wat ze aanroepen. Ze doen dit door $WhatIfPreference op $true in te stellen of $ConfirmPreference op Low in te stellen binnen het lokale bereik van de functie. Wanneer u een andere functie aanroept, worden deze waarden aangeroepen.ShouldProcess
Dit werkt eigenlijk de meeste tijd correct. Wanneer u ingebouwde cmdlets of een functie in hetzelfde bereik aanroept, werkt dit. Het werkt ook wanneer u een script of functie aanroept in een scriptmodule vanuit de console.
De ene specifieke plaats waar deze 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 een algemene regel samen te vatten is dat dit correct werkt voor binaire modules en vertrouw er nooit op dat het werkt 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 een GitHub RFC in ontwikkeling om dit probleem aan te pakken. 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 heeft me lang geduurd om van elkaar te onderscheiden ShouldProcessShouldContinue. 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 je dit bericht leuk vindt, deel dan je gedachten met mij op Twitter via de onderstaande link. Ik hoor altijd graag van mensen die waarde krijgen uit mijn inhoud.