<This is a super-important issue so you should definitely start using this in your scripts that you share with others (that have side effects on the system). Please try it out and blog about it to others so that it becomes a community norm. Thanks-jps>
One of the greatest things about PowerShell is that when you use a Cmdlet which is going to have a side effect on the system, you can always type –Whatif, -Confirm or –Verbose. Sadly, you lose this option when it comes to scripts. Over time we want to makes scripts be the semantic equivalent of Cmdlets – that is to say that a Cmdlet is a Cmdlet independent of whether you implement it in C#, VB.NET or PowerShell. That said, it takes us a while to get releases out and it really torques me that most scripts don't support –Whatif, -Confirm, and –Verbose so I decided to do something about it. I wrote a PowerShell fuction, Should-Process, which helps you implement these in your scripts (this is very similar to how we do it in Cmdlets). First let me show you a script using it and a transcript of using that script. This is a script which stops all the running calc programs:
function Stop-Calc ([Switch]$Verbose, [Switch]$Confirm, [Switch]$Whatif){$AllAnswer = $nullforeach ($p in Get-Process calc){ if (Should-Process Stop-Calc $p.Id ([REF]$AllAnswer) "`n***Are you crazy?" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif){ Stop-Process $p.Id}}}
Here is a transcript of using that function:
PS> calc;calc;calcPS> Stop-Calc -WhatifWhat if: Performing operation "Stop-Calc" on Target "436"What if: Performing operation "Stop-Calc" on Target "3776"What if: Performing operation "Stop-Calc" on Target "4104"PS>PS>PS>PS> Stop-Calc -VerboseVERBOSE: Performing "Stop-Calc" on Target "436".VERBOSE: Performing "Stop-Calc" on Target "3776".VERBOSE: Performing "Stop-Calc" on Target "4104".PS> calc;calc;calcPS>PS>PS>PS> Stop-Calc -ConfirmConfirmAre you sure you want to perform this action?Performing operation "Stop-Calc" on Target "4596".***Are you crazy?[Y] Yes [A] Yes to All [N] No [L] No to all [S] Suspend [?] Help (default is "Y"): nConfirmAre you sure you want to perform this action?Performing operation "Stop-Calc" on Target "5492".***Are you crazy?[Y] Yes [A] Yes to All [N] No [L] No to all [S] Suspend [?] Help (default is "Y"): sPS> gps calcHandles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName------- ------ ----- ----- ----- ------ -- -----------55 3 1252 5148 49 0.02 4596 calc55 3 1248 4936 49 0.06 5492 calc55 3 1248 5168 49 0.06 5588 calcPS> exitConfirmAre you sure you want to perform this action?Performing operation "Stop-Calc" on Target "5492".***Are you crazy?[Y] Yes [A] Yes to All [N] No [L] No to all [S] Suspend [?] Help (default is "Y"): lPS>PS>PS>PS> Stop-Calc -ConfirmConfirmAre you sure you want to perform this action?Performing operation "Stop-Calc" on Target "4596".***Are you crazy?[Y] Yes [A] Yes to All [N] No [L] No to all [S] Suspend [?] Help (default is "Y"): ?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"): aPS>
Here is the function that makes this all possible:
Function Should-Process ($Operation, $Target, [REF]$AllAnswer, $Warning = "", [Switch]$Verbose, [Switch]$Confirm, [Switch]$Whatif){# Check to see if "YES to All" or "NO to all" has previously been selected# Note that this technique requires the [REF] attribute on the variable.# Here is an example of how to use this:# function Stop-Calc ([Switch]$Verbose, [Switch]$Confirm, [Switch]$Whatif)# {# $AllAnswer = $null# foreach ($p in Get-Process calc)# { if (Should-Process Stop-Calc $p.Id ([REF]$AllAnswer) "`n***Are you crazy?" -Verbose:$Verbose -Confirm:$Confirm -Whatif:$Whatif)# { Stop-Process $p.Id# }# }# }if ($AllAnswer.Value -eq $false){ return $false}elseif ($AllAnswer.Value -eq $true){ return $true}if ($Whatif){ Write-Host "What if: Performing operation `"$Operation`" on Target `"$Target`""return $false}if ($Confirm){$ConfirmText = @"ConfirmAre you sure you want to perform this action?Performing operation "$Operation" on Target "$Target". $Warning"@Write-Host $ConfirmTextwhile ($True){$answer = Read-Host @"[Y] Yes [A] Yes to All [N] No [L] No to all [S] Suspend [?] Help (default is "Y")"@switch ($Answer){"Y" { return $true}"" { return $true}"A" { $AllAnswer.Value = $true; return $true }"N" { return $false }"L" { $AllAnswer.Value = $false; return $false }"S" { $host.EnterNestedPrompt(); Write-Host $ConfirmText }"?" { Write-Host @"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."@}}}}if ($verbose){Write-Verbose "Performing `"$Operation`" on Target `"$Target`"."}return $true}
I'm including this as an attachment to this entry just in case you have any copy/paste issues.
Use this often – it works great.
BTW – this all depends upon a technique that is not well documented – the [REF] attribute on parameters. Yes – PowerShell supports reference parameters! This is how we can set a variable in the Should-Process function in a way that that change is seen in the parent's scope. We do that in order to handle the YES-TO-ALL/NO-TO-ALL feature. We need to change state and then see that change the next time we get called. Notice that this requires you to pass a reference variable using a very specific syntax ([REF]$AllAnswer). Then when we want to access this parameter, we have to access it's VALUE – e.g. $AllAnswer.Value . This deserves an entire blog entry for itself but I needed to use it here so I thought a quick explanation was in order.
Jeffrey Snover [MSFT]Windows PowerShell/MMC ArchitectVisit the Windows PowerShell Team blog at: http://blogs.msdn.com/PowerShellVisit the Windows PowerShell ScriptCenter at: http://www.microsoft.com/technet/scriptcenter/hubs/msh.msp