如何在第一个错误时停止PowerShell脚本?


Answers:


303

$ErrorActionPreference = "Stop" 将带您进入那里(例如,这对于cmdlet非常有用)。

但是,对于EXE,您将需要$LastExitCode在每次exe调用后检查自己,并确定是否失败。不幸的是,我不认为PowerShell可以在这里提供帮助,因为在Windows上,EXE在构成“成功”或“失败”退出代码的内容上并不一致。多数遵循UNIX标准0表示成功,但并非全部成功。在此博客文章中查看CheckLastExitCode函数。您可能会发现它很有用。


3
$ErrorActionPreference = "Stop"行为良好的程序是否有效(成功返回0)?
2012年

14
不,它根本不适用于EXE。它仅适用于在进程内运行的PowerShell cmdlet。这有点痛苦,但是您必须在每次EXE调用后检查$ LastExitCode,对照预期的退出代码进行检查,如果该测试表明失败,则必须抛出终止脚本的执行,例如throw "$exe failed with exit code $LastExitCode",其中$ exe只是执行此操作的路径EXE。
基思·希尔

2
已接受,因为它包含有关如何使其与外部程序一起使用的信息。
安德烈斯·里奥弗里奥


5
请注意,psake具有一个名为“ exec”的命令行开关,您可以使用它来包装对外部程序的调用,并检查LastExitCode并显示错误(并在需要时停止)
enorl76

68

您应该能够通过$ErrorActionPreference = "Stop"在脚本开头使用语句来完成此操作。

默认设置$ErrorActionPreferenceContinue,这就是为什么您看到脚本在发生错误后继续运行的原因。


21
这不影响程序,仅影响cmdlet。
乔伊

18

可悲的是,由于像New-RegKey和Clear-Disk这样的cmdlet出错,这些答案都不足够。我目前在任何powershell脚本的顶部都选择了以下几行,以保持理智。

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'

然后任何本地调用都会得到这种处理:

native_call.exe
$native_call_success = $?
if (-not $native_call_success)
{
    throw 'error making native call'
}

这种本地调用模式对我来说已经变得越来越普遍,我可能应该研究使其更简洁的选项。我仍然是新手,欢迎提出建议。


14

您需要对Powershell函数和调用exe进行略有不同的错误处理,并且需要确保告诉脚本的调用者该脚本已失败。Exec在库Psake的基础上构建的,具有以下结构的脚本将在发生所有错误时停止,并且可用作大多数脚本的基本模板。

Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"


# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
  This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
  to see if an error occcured. If an error is detected then an exception is thrown.
  This function allows you to run command-line programs without having to
  explicitly check the $lastexitcode variable.
.EXAMPLE
  exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
    [CmdletBinding()]
    param(
        [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
        [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
    )
    & $cmd
    if ($lastexitcode -ne 0) {
        throw ("Exec: " + $errorMessage)
    }
}

Try {

    # Put all your stuff inside here!

    # powershell functions called as normal and try..catch reports errors 
    New-Object System.Net.WebClient

    # call exe's and check their exit code using Exec
    Exec { setup.exe }

} Catch {
    # tell the caller it has all gone wrong
    $host.SetShouldExit(-1)
    throw
}

调用eg:,由于尝试将其解释为Cmdlet参数Exec { sqlite3.exe -bail some.db "$SQL" }-bail导致错误?用引号将内容包装起来似乎无效。有任何想法吗?
rcoup

有没有办法将此代码放在中央位置,以便在要使用Exec方法时可以执行某种#include动作?
NickG

10

对@alastairtree 的答案进行了轻微修改:

function Invoke-Call {
    param (
        [scriptblock]$ScriptBlock,
        [string]$ErrorAction = $ErrorActionPreference
    )
    & @ScriptBlock
    if (($lastexitcode -ne 0) -and $ErrorAction -eq "Stop") {
        exit $lastexitcode
    }
}

Invoke-Call -ScriptBlock { dotnet build . } -ErrorAction Stop

这里的主要区别是:

  1. 它使用动词名词(模仿Invoke-Command
  2. 意味着它使用的电话运营商在幕后
  3. 模拟 -ErrorAction内置cmdlet中的行为
  4. 使用相同的退出代码退出,而不是使用新消息引发异常

1
如何传递参数/变量?例如Invoke-Call { dotnet build $something }
Michael Blake

1
@MichaelBlake您的询问非常正确,允许参数通过将使这种方法成为黄金。我正在检查adamtheautomator.com/pass-hashtables-invoke-command-argument来调整Invoke-Call以支持参数传递。如果成功,我将在此处将其发布为另一个答案。
Maxim V. Pavlov

为什么在调用过程中使用splatting运算符?那能给你什么?& @ScriptBlock& $ScriptBlock似乎做同样的事情。在这种情况下,还无法通过谷歌搜索有什么区别
pinkfloydx33

6

我是Powershell的新手,但这似乎是最有效的:

doSomething -arg myArg
if (-not $?) {throw "Failed to doSomething"}

2

我来这里寻找相同的东西。$ ErrorActionPreference =“ Stop”会立即终止我的外壳程序,而我更希望看到错误消息(暂停)在终止之前。退回我的批次灵敏度:

IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF

我发现这对于我的特定ps1脚本几乎相同:

Import-PSSession $Session
If ($? -ne "True") {Pause; Exit}

0

重定向stderrstdout似乎也不需要任何其他命令/脚本块包装器,尽管我找不到解释它为何如此工作的方法。

# test.ps1

$ErrorActionPreference = "Stop"

aws s3 ls s3://xxx
echo "==> pass"

aws s3 ls s3://xxx 2>&1
echo "shouldn't be here"

这将按预期输出以下内容(命令aws s3 ...返回$LASTEXITCODE = 255

PS> .\test.ps1

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
==> pass
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.