PowerShell betiğini ilk hatada nasıl durdurabilirim?


250

PowerShell betiğimin çalıştırdığım komutlardan herhangi biri başarısız olduğunda ( set -ebash gibi ) durmasını istiyorum . Hem Powershell komutlarını ( New-Object System.Net.WebClient) hem de programları ( .\setup.exe) kullanıyorum.

Yanıtlar:


304

$ErrorActionPreference = "Stop" Orada yolun bir parçası alacak (yani bu cmdlets için harika çalışır).

Ancak EXE'ler için $LastExitCodeher exe çağrısından sonra kendinizi kontrol etmeniz ve başarısız olup olmadığını belirlemeniz gerekir. Ne yazık ki, PowerShell'in burada yardımcı olabileceğini düşünmüyorum, çünkü Windows'ta EXE'ler bir "başarı" veya "başarısızlık" çıkış kodu oluşturan şeyde çok tutarlı değil. Çoğu, UNIX standardı 0'ı izleyerek başarıyı gösterir, ancak hepsi bunu yapmaz. Check out Bu blog yayınında CheckLastExitCode fonksiyonunu . Yararlı bulabilirsiniz.


3
$ErrorActionPreference = "Stop"İyi davranılmış programlar için işe yarıyor mu (bu başarıya 0 döndürür)?
Andres Riofrio

14
Hayır, EXE'ler için hiç çalışmıyor. Yalnızca işlem sırasında çalışan PowerShell cmdlet'leri için çalışır. Bu bir tür acıdır, ancak her EXE çağrısından sonra $ LastExitCode'u kontrol etmelisiniz, beklenen çıkış koduna karşı kontrol edin ve bu test başarısızlık gösteriyorsa, komut dosyasının yürütülmesini sonlandırmak için atmanız gerekir, örneğin throw "$exe failed with exit code $LastExitCode"$ exe sadece yol EXE.
Keith Hill

2
Harici programlarla nasıl çalışılacağı hakkında bilgi içerdiğinden kabul edildi.
Andres Riofrio


5
psake'in "exec" adlı bir komut dosyasına sahip olduğunu ve LastExitCode için bir kontrolle harici programlara çağrıları sarmak ve bir hata görüntülemek (ve istenirse durmak) için kullanabileceğiniz bir komut dosyasına sahip olduğunu unutmayın
enorl76

68

Bunu $ErrorActionPreference = "Stop"komut dosyalarınızın başındaki ifadeyi kullanarak başarabilmelisiniz .

Varsayılan ayar $ErrorActionPreferenceDİR Continuehataları oluşuyor sonra size komut görüyoruz nedenle, devam edin.


21
Bu programları etkilemez, sadece cmdlet'leri etkiler.
Joey

19

Ne yazık ki, New-RegKey ve Clear-Disk gibi buggy cmdlet'leri nedeniyle , bu cevapların hiçbiri yeterli değildir. Şu anda akıl sağlığımı korumak için herhangi bir powershell betiğinin en üstünde aşağıdaki satırlara yerleştim.

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

ve sonra herhangi bir yerel çağrı bu tedaviyi alır:

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

O yerel arama modeli yavaş yavaş benim için yeterince yaygın hale geliyor, muhtemelen daha kısa yapmak için seçeneklere bakmalıyım. Ben hala bir powershell acemiyim, bu yüzden önerilerinizi bekliyoruz.


Bu işe yarıyor - teşekkürler!
A br

14

Powershell işlevleri ve exe'yi çağırmak için biraz farklı hata işlemeye ihtiyacınız vardır ve betiğinizin arayanına başarısız olduğunu bildirdiğinizden emin olmanız gerekir. Üstünde Bina Execkütüphane Psake, tüm hatalarda durur aşağıda yapıya sahiptir ve çoğu komut dosyaları için bir temel şablonu olarak kullanılabilen bir komut dosyasından.

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
}

Örneğini çağırmak Exec { sqlite3.exe -bail some.db "$SQL" }, -bailbir Cmdlet parametresi olarak yorumlamaya çalıştığından bir hataya neden oluyor mu? Bir şeyleri tırnak içine almak işe yaramıyor. Herhangi bir fikir?
rcoup

Exec yöntemini kullanmak istediğinizde #include bir tür yapabilirsiniz böylece bu kodu merkezi bir yere koymak için bir yolu var mı?
NickG

10

@Alastairtree'nin cevabında küçük bir değişiklik :

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

Buradaki temel farklar:

  1. Fiil-İsim kullanır (taklit Invoke-Command)
  2. çağrı operatörünü kapakların altında kullandığını ima eder
  3. -ErrorActionyerleşik cmdlet'lerden gelen davranışı taklit eder
  4. yeni mesajla istisna atmak yerine aynı çıkış koduyla çıkar

1
Parametreleri / değişkenleri nasıl iletirsiniz? örneğinInvoke-Call { dotnet build $something }
Michael Blake

1
@MichaelBlake sorgunuz çok doğru, bir paraşüt geçişine izin vermek bu yaklaşımı altın yapardı. Ben teftiş ediyorum adamtheautomator.com/pass-hashtables-invoke-command-argument parametreler geçiş desteklemek için ınvoke Çağrı ayarlayın. Başarılı olursam, bunu başka bir cevap olarak göndereceğim.
Maxim V. Pavlov

Çağırma sırasında neden sıçrayan operatörü kullanıyorsunuz? Bu sana ne kazandırır? & @ScriptBlockve & $ScriptBlockaynı şeyi yapıyor gibi görünüyor. Bu durumda farkın ne olduğunu
Google'a bildiremedim

6

Powershell'de yeniyim ama bu en etkili gibi görünüyor:

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

2

Buraya aynı şeyi aramaya geldim. $ ErrorActionPreference = "Durdur", sonlandırılmadan önce hata iletisini (duraklatma) görmek istediğimde kabuğumu hemen öldürür. Toplu hassasiyetlerime geri dönüyorum:

IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF

Bu benim belirli ps1 betiği için hemen hemen aynı çalışır bulundu:

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

0

Yönlendirme stderr, stdoutdiğer komutlar / scriptblock sarmalayıcıları olmadan da hile yapıyor gibi görünüyor, ancak neden bu şekilde çalıştığına dair bir açıklama bulamıyorum ..

# test.ps1

$ErrorActionPreference = "Stop"

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

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

Bu, beklendiği gibi çıktı verir (komut aws s3 ...geri döner $LASTEXITCODE = 255)

PS> .\test.ps1

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
==> pass
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.