302 votes

Comment arrêter un script PowerShell à la première erreur ?

Je veux que mon script PowerShell s'arrête lorsque l'une des commandes que j'exécute échoue (par exemple set -e dans bash). J'utilise les deux commandes Powershell ( New-Object System.Net.WebClient ) et des programmes ( .\setup.exe ).

363voto

Keith Hill Points 73162

$ErrorActionPreference = "Stop" vous permettra de faire une partie du chemin (c'est-à-dire que cela fonctionne très bien pour les cmdlets).

Cependant, pour les EXEs, vous devrez vérifier les points suivants $LastExitCode après chaque invocation de l'exe et déterminer si cela a échoué ou non. Malheureusement, je ne pense pas que PowerShell puisse vous aider ici, car sous Windows, les EXE ne sont pas très cohérents sur ce qui constitue un code de sortie "succès" ou "échec". La plupart d'entre eux suivent le standard UNIX de 0 indiquant le succès, mais pas tous. Consultez la page La fonction CheckLastExitCode dans ce billet de blog . Vous pourriez trouver cela utile.

3 votes

Fait $ErrorActionPreference = "Stop" fonctionne-t-il pour les programmes qui se comportent bien (qui renvoient 0 en cas de succès) ?

18 votes

Non, cela ne fonctionne pas du tout pour les EXEs. Il ne fonctionne que pour les cmdlets PowerShell qui s'exécutent in-process. C'est un peu pénible mais vous devez vérifier $LastExitCode après chaque invocation d'EXE, le comparer au code de sortie attendu et si ce test indique un échec, vous devez lancer pour terminer l'exécution du script par exemple. throw "$exe failed with exit code $LastExitCode" où $exe est simplement le chemin vers l'EXE.

2 votes

Accepté parce qu'il inclut des informations sur la façon de le faire fonctionner avec des programmes externes.

73voto

goric Points 5230

Vous devriez être en mesure d'accomplir ceci en utilisant l'instruction $ErrorActionPreference = "Stop" au début de vos scripts.

Le paramètre par défaut de $ErrorActionPreference es Continue C'est pourquoi vous voyez vos scripts continuer à fonctionner après que des erreurs se soient produites.

25 votes

Cela n'affecte pas les programmes, seulement les cmdlets.

32voto

aggieNick02 Points 194

Tristement, à cause de cmdlets bogués comme New-RegKey et Clear-Disk. aucune de ces réponses n'est suffisante. J'ai actuellement choisi le code suivant dans un fichier appelé ps_support.ps1 :

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
function ThrowOnNativeFailure {
    if (-not $?)
    {
        throw 'Native Failure'
    }
}

Ensuite, dans n'importe quel fichier powershell, après le CmdletBinding y Param pour le fichier (si présent), j'ai ce qui suit :

$ErrorActionPreference = "Stop"
. "$PSScriptRoot\ps_support.ps1"

Le duplicata ErrorActionPreference = "Stop" La ligne est intentionnelle. Si j'ai fait une gaffe et que d'une manière ou d'une autre j'ai obtenu le chemin vers ps_support.ps1 faux, qui ne doit pas échouer silencieusement !

Je garde ps_support.ps1 dans un emplacement commun à mon dépôt/espace de travail, de sorte que le chemin d'accès pour le dot-sourcing peut changer en fonction de l'emplacement du dépôt actuel. .ps1 est.

Tout appel natif reçoit ce traitement :

native_call.exe
ThrowOnNativeFailure

Le fait d'avoir ce fichier à dot-source m'a aidé à rester sain d'esprit lors de l'écriture de scripts powershell :-)

14voto

alastairtree Points 76

Vous avez besoin d'un traitement des erreurs légèrement différent pour les fonctions powershell et pour l'appel des exe, et vous devez être sûr de dire à l'appelant de votre script qu'il a échoué. Construire au-dessus de Exec de la bibliothèque Psake, un script qui a la structure ci-dessous s'arrêtera sur toutes les erreurs, et est utilisable comme modèle de base pour la plupart des scripts.

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
}

0 votes

En invoquant eg : Exec { sqlite3.exe -bail some.db "$SQL" } le -bail provoque une erreur car il essaie de l'interpréter comme un paramètre de Cmdlet ? Mettre les choses entre guillemets ne semble pas fonctionner. Avez-vous des idées ?

0 votes

Existe-t-il un moyen de placer ce code quelque part de façon centrale afin de pouvoir faire une sorte de #include lorsque l'on veut utiliser la méthode Exec ?

0 votes

Oui, tu peux. Vous pouvez le mettre dans un fichier nommé powershell-error-handling-sanity.ps1 puis placez le fichier ps1 au sommet de n'importe quel autre fichier ps1 avec la commande . <relative_or_absolute_path_to_powershell-error-handling-sani‌​ty.ps1

13voto

Lucas Points 4891

Une légère modification de la réponse de @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

Les principales différences sont les suivantes :

  1. il utilise le Verbe-Nom (mimétisme) Invoke-Command )
  2. implique qu'il utilise le opérateur téléphonique sous les couvertures
  3. imite -ErrorAction comportement des cmdlets intégrés
  4. sort avec le même code de sortie plutôt que de lancer une exception avec un nouveau message

1 votes

Comment passer des paramètres / variables ? par exemple Invoke-Call { dotnet build $something }

1 votes

@MichaelBlake votre demande est très juste, permettre un passage des paramètres rendrait cette approche excellente. J'inspecte adamtheautomator.com/pass-hashtables-invoke-command-argument pour ajuster le Invoke-Call afin de supporter le passage des paramètres. Si je réussis, je le posterai comme une autre réponse ici.

1 votes

Pourquoi utilisez-vous l'opérateur splatting pendant l'invocation ? Qu'est-ce que cela vous apporte ? & @ScriptBlock y & $ScriptBlock semblent faire la même chose. Je n'ai pas réussi à trouver sur Google quelle est la différence dans ce cas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X