“我并不总是测试我的脚本,但当我这么做时,我是在生产环境中进行的”
别撒谎。在某个时候,我们都曾经这样做过。但在生产环境中运行PowerShell脚本并不一定有风险。只需记住使用内置的PowerShell WhatIf参数!
在PowerShell脚本对生产环境进行更改之前,了解该PowerShell命令会做什么是不是件好事?想象一下在执行命令之前能够询问:“如果你要运行,你会做什么?”您可以使用WhatIf
参数。
所有编译的PowerShell cmdlet都包含一个名为WhatIf
的参数。该参数帮助您评估命令是否会按照您的预期工作,或者是否会引发核反应堆的融化。
方便的WhatIf
参数不仅适用于所有内置的cmdlet,而且也适用于您的脚本和高级功能!如果您的代码将更改环境中的内容,您就可以选择实施它,以确保有一个安全机制。
先决条件
本文将进行逐步介绍。如果您想在这个过程中跟随,您应该有一些事先准备好的东西。
- A Windows computer capable of running Powershell v5.1. (Windows 10 and above recommended)
- A script editor like Notepad++, the Windows PowerShell ISE or Visual Studio Code.
请注意,在本文中,我将在Windows 10计算机上使用Visual Studio Code 1.38(2019年8月)。
WhatIf PowerShell参数:定义
简而言之,WhatIf
参数是所有高级函数和cmdlet(通过将PowerShell CmdletBinding
关键字添加到脚本和函数中)都可用的内置开关参数。当使用时,该命令会向控制台报告命令的预期效果。但实际上不会执行该命令。
所有cmdlet和高级函数都具有可用的WhatIf参数。在本文中,我们将使用Get-Service
、Stop-Service
和New-Item
cmdlet演示此参数。
检查PowerShell WhatIf支持
如果您不确定特定命令是否支持WhatIf
,有两种快速检查的方法。
使用Get-Command
您可以使用Get-Command
命令通过使用下面显示的Syntax
参数查看命令元数据。如果看到-WhatIf
引用,则支持WhatIf
。

Get-Command <CommandName> -Syntax
使用Tab Completion
您还可以通过使用制表符补全检查WhatIf
参数的支持。只需在PowerShell控制台中键入要检查的命令,然后加上空格、破折号、’Wh’和制表键。
如果出现WhatIf
,则说明该命令具有WhatIf
参数。

PS> <CommandName> -Wh[tab]
使用PowerShell WhatIf参数与Cmdlet
有许多不同的方法可以利用WhatIf
参数。在这一部分,您将学习如何立即开始使用内置的cmdlet来使用WhatIf
参数。
创建文件
像所有的cmdlet一样,New-Item
cmdlet有一个WhatIf
参数。在这个例子中,您将使用New-Item
cmdlet来创建一个名为newfile1.txt的文件,该文件位于相同的工作目录中。
如果您运行下面的命令,它将创建名为newfile.txt的文件。

PS51> New-Item -ItemType File -Path .\newfile1.txt
但如果此命令创建的文件可能会导致问题,该怎么办?没问题。您可以在最后添加WhatIf
参数。

New-Item -ItemType File -Path .\newfile1.txt -WhatIf
停止服务
您还可以使用Stop-Service
cmdlet来使用WhatIf
参数。在这个例子中,您正在获取前五个服务的列表,并使用Stop-Service
命令停止它们。但您并没有。
相反,您只是在PowerShell控制台看到输出消息,让您知道Stop-Service
cmdlet将会停止哪些服务。
PS51> (Get-Service)[0..4] | Stop-Service -WhatIf
全局更改Powershell WhatIf行为
到目前为止,您应该知道仅使用WhatIf
参数与cmdlet或高级函数模拟操作。您正在影响命令级别的WhatIf
行为。
WhatIf
行为还可以在影响所有命令的更高级别进行设置,方法是通过操作自动变量$WhatIfPreference
。
$WhatIfPreference
变量是布尔型的,只能是True
或False
。默认情况下,它设置为False,表示WhatIf
支持在所有命令上都被禁用,除非在命令级别被覆盖。如果设置为True
,所有支持它的命令,无论是明确使用WhatIf
参数还是不使用,都将处于“WhatIf
模式”中。
您可以通过通过$WhatIfPreference = $true
更改$WhatIfPreference
的值来测试此功能。您可以看到,使用New-Item
不带WhatIf
参数现在就像传递了该参数一样。

PS51> $WhatIfPreference = $true
如果您已将
$WhatIfPreference
更改为True
,请不要忘记通过$WhatIfPreference = $false
将其改回False
。
在本节中,您学会了如何在现有的cmdlet中使用WhatIf支持。在下一步中,您将学习如何在自定义脚本和函数中构建WhatIf支持。
在函数中实现PowerShell WhatIf支持
在尝试在脚本中实现WhatIf
支持 之前,了解有错误和正确的方法是至关重要的。您将在接下来的部分看到它们是什么。
错误的做法:不要重复造轮子
脚本开发人员常常会重复造轮子,使用一些if/then逻辑来实现自己的WhatIf
支持。下面是一个示例。
请注意,开发人员已经定义了自己的WhatIf
开关参数。然后,使用该参数的值,他们使用了if/then结构来处理当使用或不使用WhatIf
参数时的逻辑。
当使用如下所示的WhatIf参数运行上述函数时,看起来它完成了它的工作。该函数实际上并未删除任何内容,并向控制台返回了一条消息。

PS51> Remove-LogFile -name log.txt -WhatIf
如果它能运行,那么这种方法有什么问题呢?因为您忽略了高级函数的内置功能。您将需要将此功能集成到您创建的每个函数中,而不仅仅专注于在关闭时命令将执行什么操作。
相反,不要重复造轮子,使用SupportsShouldProcess
关键字与$PSCmdlet.ShouldProcess()
结合起来。就是这样。
正确做法:使用SupportsShouldProcess
所有使用[CmdletBinding()]
关键字的函数都会使它们变成“高级”函数。这个关键字为函数添加了各种功能,包括WhatIf
支持。
所有高级函数都支持WhatIf
功能,但是你可以选择是否利用它。要这样做,你必须首先在[CmdletBinding()]
括号中使用SupportsShouldProcess
关键字,就像下面所示。
现在,该函数允许你在$PSCmdlet
函数变量上调用ShouldProcess()
方法,以确定WhatIf
参数是否传递给了函数。当使用WhatIf
参数时,ShouldProcess()
返回False
。否则,它将始终返回True
。
WHATIF PARAMETER USED | SHOULDPROCESS() RESULT |
---|---|
True | False |
False | True |
现在您可以看到,当使用WhatIf
参数执行Remove-LogFile
时,它显示与内置cmdlet相同的行为。

PS51> Remove-LogFile -name log.txt -WhatIf
摘要
在本文中,您了解了PowerShell的WhatIf参数。您现在应该了解它是如何工作的以及它可以带来什么好处。
您还应该知道,下次在需要为PowerShell函数添加安全保障时,不要重新发明轮子。相反,利用现有的PowerShellWhatIf
支持!