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

And*_*rio 226 windows powershell

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

Kei*_*ill 272

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

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

  • 不,它对EXE根本不起作用.它仅适用于在进程中运行的PowerShell cmdlet.这有点痛,但你必须在每次EXE调用后检查$ LastExitCode,检查是否符合预期的退出代码,如果该测试表明失败,你必须抛出以终止执行脚本,例如`throw'$ exe失败退出code $ LastExitCode"`其中$ exe只是EXE的路径. (14认同)
  • 我在这里提出了一个功能请求:https://connect.microsoft.com/PowerShell/feedback/details/751703/option-to-stop-script-if-command-line-exe-fails (4认同)
  • 请注意,psake有一个名为"exec"的命令行开关,您可以使用它来检查对外部程序的调用,并检查LastExitCode并显示错误(如果需要,可以停止) (4认同)
  • `$ ErrorActionPreference ="停止"`是否适用于表现良好的程序(成功时返回0)? (3认同)
  • 接受,因为它包含有关如何使其与外部程序一起使用的信息. (2认同)
  • 许多年后,现在有一个 [RFC](https://github.com/PowerShell/PowerShell-RFC/pull/88/files) 也引入了对外部程序的 abort-on-failure 支持。 (2认同)

gor*_*ric 62

您应该能够通过使用$ErrorActionPreference = "Stop"脚本开头的语句来完成此任务.

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

  • 这不会影响程序,只会影响cmdlet. (18认同)

agg*_*k02 13

遗憾的是,由于像New-RegKey和Clear-Disk这样的小错误的cmdlet,这些答案都不够.我现在已经确定了以下任何PowerShell脚本顶部的行,以保持我的理智.

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
Run Code Online (Sandbox Code Playgroud)

然后任何本地电话获得这种待遇:

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

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


ala*_*ree 11

您需要对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
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*r L 9

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

doSomething -arg myArg
if (-not $?) {throw "Failed to doSomething"}
Run Code Online (Sandbox Code Playgroud)


Luc*_*cas 7

对@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
Run Code Online (Sandbox Code Playgroud)

这里的主要区别是:

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

  • 为什么在调用期间使用splatting 运算符?这会给你带来什么?`&amp; @ScriptBlock` 和 `&amp; $ScriptBlock` 似乎做了同样的事情。无法通过谷歌搜索这种情况下有什么区别 (2认同)

Nas*_*out 7

对于 2021 年来到这里的人们,这是我的解决方案,涵盖 cmdlet 和程序

function CheckLastExitCode {
    param ([int[]]$SuccessCodes = @(0))

    if (!$?) {
        Write-Host "Last CMD failed $LastExitCode" -ForegroundColor Red
        #GoToWrapperDirectory in my code I go back to the original directory that launched the script
        exit
    }

    if ($SuccessCodes -notcontains $LastExitCode) {
        Write-Host "EXE RETURNED EXIT CODE $LastExitCode" -ForegroundColor Red
        #GoToWrapperDirectory in my code I go back to the original directory that launched the script
        exit
    } 
    
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它

cd NonExistingpath
CheckLastExitCode
Run Code Online (Sandbox Code Playgroud)


nic*_*van 5

据我所知,Powershell 没有对其调用的子程序返回的非零退出代码进行任何自动处理。

到目前为止,我所知道的模仿行为的唯一解决方案是在每次调用外部命令bash -e后添加此检查:

if(!$?) { Exit $LASTEXITCODE }
Run Code Online (Sandbox Code Playgroud)

  • @bounav:嗯,这是相对的。我们只能说这是我找到的“不太糟糕”的解决方案。另一方面,在 bash 脚本中,您只需在每个脚本的开头添加“#!/bin/bash -e”,它始终适用于整个脚本。我真的很讨厌 Powershell 只是因为它愚蠢的错误处理。(但不幸的是我有时没有太多选择使用它......) (4认同)