Powershell:如何在脚本中停止错误消息的显示?

135

当我的PowerShell脚本试图为不存在的服务器(在我的情况下是“bla”)创建SQL Server对象时,PowerShell会显示许多红色的PowerShell错误。

由于我的脚本在此类调用之后检查$?的值,并显示和记录错误,因此我不想再显示几行PowerShell错误。

我应该如何禁止这些错误显示在我的脚本中?

9个回答

195

您有几个选择。 最简单的方法涉及使用ErrorAction设置。

-Erroraction是所有cmdlets的通用参数。 如果有特殊命令要忽略,可以使用-erroraction 'silentlycontinue',这将基本上忽略该命令生成的所有错误消息。 您还可以使用Ignore值(在PowerShell 3+中):

与SilentlyContinue不同,Ignore不会将错误消息添加到$Error自动变量中。

如果要忽略脚本中的所有错误,则可以使用系统变量$ErrorActionPreference并执行相同操作:$ErrorActionPreference= 'silentlycontinue'

有关-ErrorAction的更多信息,请参见about_CommonParameters。 有关$ErrorActionPreference的更多信息,请参见about_preference_variables


1
你需要在“-ErrorAction 'SilentlyContinue'”中使用单引号吗?Intellisense会显示选项,但不会添加单引号。 - PAS
2
如果上述格式对您不起作用,请参考上面提供的链接。在PS 5.1中,我需要将其格式化为-ErrorAction:SilentlyContinue。因为我是从批处理中调用我的cmdlet,所以我不知道这是否有所不同。但是当您知道可能会抛出可接受的错误时,这是一个好消息。 - David
2
--rm .\Windows.old\ -Force -Recurse -Verbose -ErrorAction SilentlyContinue -WarningAction SilentlyContinue - Tertius Geldenhuys

26

您还可以在命令后附加2>$null

例如:

$rec = Resolve-DnsName $fqdn -Server $dns 2>$null

4
虽然这可能不是“正确”的方法,但它非常适合处理无论错误优先级开关或变量如何都输出错误的 CmdLet。我刚刚在处理 Get-CsOnlineVoiceUser 版本 3.1.1 时用到了这个方法,该版本必须在底层使用 Get-CsUser,但没有正确处理其错误。 - Nathan Hartley
这个解决方案适用于在PowerShell中执行非PowerShell命令的情况。例如,只有这个解决方案在调用“taskkill”时才能正常工作。 - Oleksandr Novik
那么我如何对每一行进行选择性操作呢?我有一个简单的脚本,用于恢复我的快速访问钉住,但有时我会移动文件夹,这些特定条目的命令只指向空对象,因此会出现错误...这没关系。我似乎无法以这种方式使其工作...只能使用try catch,这比2>$null要多写一点,请原谅我的懒惰。 - JasonXA

24

Windows PowerShell提供了两种报告错误的机制:一种用于终止错误,另一种用于非终止错误。

内部CmdLets代码可能会在发生错误时调用ThrowTerminatingError方法,这些错误可能不允许命令继续处理其输入对象。脚本编写者可以使用异常来捕获这些错误。

例如:

try
{
  Your database code
}
catch
{
  Error reporting/logging
}

在处理输入对象时,内部 CmdLets 代码可以调用 WriteError 方法报告非终止错误。脚本编写者可以使用 -ErrorAction 选项隐藏这些消息,或使用 $ErrorActionPreference 设置整个脚本的行为。


8

在您的脚本中添加 -ErrorAction SilentlyContinue,就可以顺利运行了。


你如何在脚本中添加-ErrorAction SiltentlyContinue?你能发一个例子吗? - octopusgrabbus
使用此标志,脚本可以在没有错误的情况下继续运行,但对于某些命令仍可能打印错误消息。对于此命令"$result = Test-Path $path -ErrorAction SilentlyContinue",它会继续执行而不打印错误消息,但对于此"$AppPool = Get-WebAppPoolState $sitename -ErrorAction SilentlyContinue",它会继续执行并打印错误消息。 - Yogesh Jindal
当前一个操作失败时,让程序继续运行几乎总是一个坏主意。在操作失败后,程序很少有机会执行任何有用的操作。 - jpmc26

8

你的想法偏离了正确的方向。几乎从来不建议消除错误,并在每个命令之后显式地手动检查$? 是非常麻烦和容易被遗忘(易出错)的。不要让自己轻易犯错。如果你得到大量红色错误,这意味着你的脚本应该停止运行,而不是继续执行。如果大多数命令都失败了,它就不能再执行有用的工作了。当程序和系统处于未知状态时,继续运行程序将产生未知的后果;你很容易会让系统处于一种损坏状态。

正确的解决方案是在第一个错误发生时停止程序。这个原则被称为“快速失败”,PowerShell 有一种内置机制来启用这种行为。它是一个叫做“error preference”的设置,将它设置为最高级别将使你的脚本(以及子范围,如果它们没有覆盖该设置)表现出这种行为:

$ErrorActionPreference = 'Stop'

这样做可以为您生成一个漂亮的、大的错误消息,防止在第一次出现问题时执行以下命令,而不必每次运行命令时都检查$?。这使得代码更加简单可靠。我会把它放在我写的每一个脚本的顶部,你也应该这样做。
在极少数情况下,您可以绝对确定允许脚本继续运行是有意义的,您可以使用以下两种机制之一:
- catch: 这是更好、更灵活的机制。您可以在多个命令周围包装一个try/catch块,允许第一个错误停止序列并跳转到处理程序,在那里您可以记录它,然后以其他方式从中恢复或重新抛出它以进一步传递错误。您还可以限制catch只针对特定错误,这意味着它只会在您预期的特定情况下被调用,而不是任何错误。(例如,因为文件已经存在而无法创建文件会引起与安全故障不同的响应。) - 常见的-ErrorAction参数: 此参数更改一个函数调用的错误处理,但您无法将其限制为特定类型的错误。只有当您可以确定脚本可以在任何错误下继续运行时,才应使用此选项。
在您的情况下,您可能希望在整个程序周围使用一个大的try/catch块。然后,在第一个错误处停止进程,catch块可以记录它并退出。这将从您的程序中删除许多重复代码,除了清理日志文件和终端输出外,还会使您的程序更不容易引起问题。
请注意,这不处理外部可执行文件失败的情况(出口代码非零,传统上),因此如果您调用任何可执行文件,则仍需要检查$LASTEXITCODE。尽管存在这种限制,但该设置仍然节省了大量的代码和精力。
额外的可靠性:
您可能还想考虑使用严格模式:
Set-StrictMode -Version Latest

这可以防止PowerShell在使用不存在的变量和其他奇怪情况下默默地执行。(有关其限制的详细信息,请参见-Version参数。)

将这两个设置结合起来使PowerShell更容易出错,这使得在其中进行编程大大简化。


3
你声称这样做会使它更可靠,但在任何自动化情况下都会产生相反的效果。有时候有些脚本应该运行,无论之前的 cmdlet 是否出错,而且分发单个 PowerShell 脚本比每个所需操作一个更容易。使用你的解决方案,如果启动时需要删除的文件不存在(因为 Windows),那么该脚本中的后续 cmdlet 将不会运行,尽管系统处于所需状态。恭喜,现在你拥有一个带有错误的破损脚本正在远程运行,而这些错误并没有被推送到日志文件中。 - IAmJersh
@TheHitchenator 处理已存在的文件的方法是编写代码以在存在时继续执行。您不需要为此消除错误; 您只需首先检查它是否存在,或者根据您需要的确切行为使用“-Force”或其他内容。如果您只是消除错误,则在由于安全策略而创建文件失败时,您的脚本将继续运行,这将导致其无法正确工作甚至可能进一步损坏系统。让一个破损的脚本继续运行比发现您忘记处理特定系统状态要危险得多。 - jpmc26
2
你实际上是在回答一个“如何禁用这个错误输出?我已经在脚本中构建了自己的错误检查”问题,而你却说“不要那样做,让错误阻止脚本。”显然,如果他们已经构建了错误检查,那么他们将处理这些问题。你没有提供帮助,只是无端地持反对态度,因为你未能设想一种环境,其中事情不按照你习惯的方式工作。请在提供答案时,确实回答问题,而不仅仅告诉别人不要做某事。 - IAmJersh
@TheHitchenator 如果以下命令都失败了(正如OP收到的大量错误所证明的那样),那么就没有理由执行它们。这与我的想象无关,而与我们所获得的证据有关。这种方法是有缺陷的,会增加工作量并增加出现问题的机会。如果没有替代方案,让人继续走下一条错误的道路是没有好处的。SO始终明确鼓励提供更好的替代方法。 - jpmc26
@TheHitchenator 如果你真的读了我的答案(包括最近的编辑前后),它确实解释了如何处理错误。 - jpmc26

8

我曾经在使用 [system.net.dns] 解析主机名时遇到了类似的问题。如果无法解析IP地址,.Net会抛出一个终止错误。

为了防止终止错误并仍然保留输出的控制权,我创建了一个使用 TRAP 的函数。

例如:

Function Get-IP 
{PARAM   ([string]$HostName="")
PROCESS {TRAP 
             {"" ;continue} 
             [system.net.dns]::gethostaddresses($HostName)
        }
}

3

扩展Mikkel的答案.

如果您仍然想捕获错误,可以使用“-ErrorAction stop”结合try-catch。

“-ErrorAction silentlycontinue”将忽略错误。

例如:

try
{
    New-Item -Path "/somepath" -Name "somename" -ErrorAction Stop | Out-Null
}
catch
{
    echo "You must run this command in an elevated mode."
}

注意:并没有“silentlyStop”动作,我认为Mickel的回答指的是“stop”动作。这很可能是一个打字错误。 使用try-catch与“stop”动作结合的想法是为了能够不仅可以忽略潜在的错误,而且在发生错误时显示一些内容。


2
在某些情况下,您可以在命令后面加上 Out-Null 以避免输出。
command | Out-Null

这不会使“错误”输出静音,只有“成功”输出。 - mklement0

-1

如果您想抑制 cmdlet 的 PowerShell 错误消息,但仍然想捕获错误,请使用“-erroraction 'silentlyStop'”


2
没有这样的操作。虽然有一个停止操作。 - Marvin Dickhaus
但它允许抑制红色错误消息,并在使用New-Item -ItemType directory(PowerShell v2.0)时仍然使用catch命令/部分 - Milan Kerslager
@MilanKerslager,您能否友好地展示一段代码示例——因为我和其他人都认为没有名为“SilentlyStop”的ActionPreference。 - FSCKur
我相信Mikkel的意思是SilentlyContinue而不是silentlyStop,因为在你想要它执行的内容上,这更有意义。 - John

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接