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

时间:2012-03-30 18:27:34

标签: windows powershell

我希望我的PowerShell脚本在我运行的任何命令失败时停止(例如bash中的set -e)。我正在使用Powershell命令(New-Object System.Net.WebClient)和程序(.\setup.exe)。

9 个答案:

答案 0 :(得分:253)

$ErrorActionPreference = "Stop"会让你在那里的一部分(即这适用于cmdlet)。

但是对于EXE,您需要在每次exe调用后自己检查$LastExitCode并确定是否失败。不幸的是,我不认为PowerShell可以在这里提供帮助,因为在Windows上,EXE在构成“成功”或“失败”退出代码的方面并不十分一致。大多数遵循UNIX标准0指示成功但不是全部。查看CheckLastExitCode function in this blog post。您可能会发现它很有用。

答案 1 :(得分:61)

您应该可以通过在脚本开头使用语句$ErrorActionPreference = "Stop"来实现此目的。

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

答案 2 :(得分:11)

可悲的是,due to buggy cmdlets like New-RegKey and Clear-Disk,这些答案都不够。我目前在任何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'
}

对我来说,原生呼叫模式正在慢慢变得普遍,我应该考虑使其更简洁的选项。我仍然是一个名副其实的新手,所以欢迎提出建议。

答案 3 :(得分:9)

对于powershell函数和调用exe函数,您需要稍微不同的错误处理,并且您需要确保告诉调用者您的脚本已失败。在Psake库的Exec之上构建,具有以下结构的脚本将停止所有错误,并可用作大多数脚本的基本模板。

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
}

答案 4 :(得分:5)

对@alastairtree中的answer进行了一些修改:

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. 暗示它在封面下使用call operator
  3. 模仿 -ErrorAction来自内置cmdlet的行为
  4. 使用相同的退出代码退出,而不是使用新消息引发异常

答案 5 :(得分:2)

我来这里寻找同样的事情。 $ ErrorActionPreference =“Stop”会在我终止之前看到错误消息(暂停)时立即终止我的shell。回到我的批处理感上:

IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF

我发现这对我的特定ps1脚本几乎一样:

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

答案 6 :(得分:1)

似乎简单的重新抛出就可以了。

param ([string] $Path, [string] $Find, [string] $Replace)
try {
  ((Get-Content -path $Path -Raw) -replace $Find, $Replace) | Set-Content -Path $Path
  Write-Output Completed.
} catch {
  # Without try/catch block errors don't interrupt program flow.
  throw
}

现在输出 Completed 只有在成功执行后才会出现。

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

在没有任何其他命令/脚本块包装的情况下,将stderr重定向到stdout似乎也可以解决问题,尽管我找不到解释为什么这样做的原因。

# 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