Як зупинити скрипт PowerShell при першій помилці?


250

Я хочу, щоб мій скрипт PowerShell зупинився, коли будь-яка команда, яку я запускаю, не виходить (як set -eу баші). Я використовую як команди Powershell ( New-Object System.Net.WebClient), так і програми ( .\setup.exe).

Відповіді:


303

$ErrorActionPreference = "Stop" отримає вам частину шляху туди (тобто це чудово підходить для cmdlets).

Однак для EXE вам потрібно буде перевірити $LastExitCodeсебе після кожного виклику EXE і визначити, не вдалося це чи ні. На жаль, я не думаю, що PowerShell може тут допомогти, оскільки в Windows, EXE не надто послідовні щодо того, що є кодом виходу "успіх" або "збій". Більшість дотримується стандарту UNIX 0, що свідчить про успіх, але не всі. Ознайомтеся з функцією CheckLastExitCode в цій публікації блогу . Ви можете вважати його корисним.


3
Чи $ErrorActionPreference = "Stop"працює для добре сприйнятих програм (які повертають 0 на успіх)?
Андрес Ріофріо

14
Ні, це взагалі не працює для EXE. Він працює лише для командлетів PowerShell, які запускаються в процесі. Це біль, але вам потрібно перевірити $ LastExitCode після кожного виклику EXE, перевірити, чи не очікується код виходу, і якщо цей тест вказує на невдачу, вам доведеться кинути, щоб припинити виконання сценарію, наприклад, throw "$exe failed with exit code $LastExitCode"де $ exe - це лише шлях до EXE
Кіт Хілл

2
Прийнято, оскільки містить інформацію про те, як змусити його працювати із зовнішніми програмами.
Андрес Ріофріо

4
Я розміщую тут запит на функцію: connect.microsoft.com/PowerShell/feedback/details/751703/…
Helephant

5
зауважте, що у psake є командна команда "exec", яку ви можете використовувати для завершення дзвінків до зовнішніх програм з чеком на LastExitCode та відображення помилки (та зупинення, за бажанням)
enorl76

68

Ви повинні мати змогу досягти цього, використовуючи оператор $ErrorActionPreference = "Stop"на початку своїх сценаріїв.

Настройка за замовчуванням - $ErrorActionPreferenceце Continue, тому ви бачите, що ваші сценарії продовжують працювати після помилок.


21
Це не стосується програм, лише командлети.
Джої

18

На жаль, через помилкові командлети типу New-RegKey та Clear-Disk , жоден із цих відповідей недостатній. На даний момент я зупинився на наступних рядках у верхній частині будь-якого сценарію повноважень, щоб підтримувати свій розум.

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

Потрібно дещо інше обробка помилок для функцій shellhell і для виклику 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
}

Викликаючи наприклад:: Exec { sqlite3.exe -bail some.db "$SQL" }, -bailвикликає помилку, оскільки вона намагається інтерпретувати її як параметр Cmdlet? Зберігати речі в лапки, схоже, не виходить. Будь-які ідеї?
rcoup

Чи є спосіб поставити цей код десь центральним, щоб ви могли зробити якесь #include, коли хочете використовувати метод Exec?
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 поведінку від вбудованих командлетів
  4. виходить із тим же кодом виходу, а не викидає виняток із новим повідомленням

1
Як ви передаєте параметри / змінні? наприкладInvoke-Call { dotnet build $something }
Майкл Блейк

1
@MichaelBlake ваш запит настільки правильний, що дозволяє парамам, що проходять через цей підхід, зробить цей підхід золотим. Я перевіряю adamtheautomator.com/pass-hashtables-invoke-command-argument для коригування виклику Invoke -Call для підтримки проходження парам . Якщо мені це вдасться, я опублікую це як іншу відповідь тут.
Павлов Максим Вікторович

Для чого ви використовуєте оператор бризки під час виклику? Що це ви отримуєте? & @ScriptBlockі, & $ScriptBlockздається, роблять те саме. Чи не вдалося Google , що різниця в даному випадку
pinkfloydx33

6

Я новачок у powerhell, але це здається найбільш ефективним:

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

2

Я прийшов сюди шукати те саме. $ ErrorActionPreference = "Стоп" вбиває мою оболонку негайно, коли я швидше побачу повідомлення про помилку (паузу), перш ніж воно закінчиться. Відпадання моєї партії чутливості:

IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF

Я виявив, що це працює майже однаково для мого конкретного сценарію ps1:

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

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.