102 votes

Comment sortir de ForEach-Object dans PowerShell

J'ai le code suivant :

$project.PropertyGroup | Foreach-Object {
    if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
        $a = $project.RemoveChild($_);
        Write-Host $_.GetAttribute('Condition')"a été supprimé.";
    }
};

Question #1 : Comment puis-je sortir de ForEach-Object ? J'ai essayé d'utiliser "break" et "continue", mais cela ne fonctionne pas.

Question #2 : J'ai découvert que je peux modifier la liste dans une boucle foreach... Nous ne pouvons pas le faire de cette manière en C#... Pourquoi PowerShell nous permet-il de le faire ?

152voto

smeltplate Points 1407

Tout d'abord, Foreach-Object n'est pas une boucle réelle et l'appel de break dedans annulera tout le script plutôt que de passer à l'instruction suivante.

En revanche, break et continue fonctionneront comme vous vous y attendez dans une vraie boucle foreach.

Élément #1. Mettre un break dans la boucle foreach sortira de la boucle, mais ne stoppera pas le pipeline. Il semble que vous vouliez quelque chose comme ceci :

$todo=$project.PropertyGroup 
foreach ($thing in $todo){
    if ($thing -eq 'some_condition'){
        break
    }
}

Élément #2. PowerShell vous permet de modifier un tableau dans une boucle foreach sur ce tableau, mais ces changements ne prendront effet qu'une fois la boucle terminée. Essayez d'exécuter le code ci-dessous pour un exemple.

$a=1,2,3
foreach ($value in $a){
  Write-Host $value
}
Write-Host $a

Je ne peux pas commenter pourquoi les auteurs de PowerShell ont permis cela, mais la plupart des autres langages de script (Perl, Python et shell) autorisent des constructions similaires.

49voto

Stoffi Points 495

Il existe des différences entre foreach et foreach-object.

Vous trouverez une très bonne description ici : MS-ScriptingGuy

Pour tester en PS, voici des scripts pour montrer la différence.

ForEach-Object:

# Omettre 5.
1..10 | ForEach-Object {
if ($_ -eq 5) {return}
# if ($_ -ge 5) {return} # Omettre à partir de 5.
Write-Host $_
}
write-host "after1"

# Annule tout le script à 15, "after2" non imprimé.
11..20 | ForEach-Object {
if ($_ -eq 15) {continue}
Write-Host $_
}
write-host "after2"

# Annule tout le script à 25, "after3" non imprimé.
21..30 | ForEach-Object {
if ($_ -eq 25) {break}
Write-Host $_
}
write-host "after3"

foreach

# Termine la boucle foreach à 5.
foreach ($number1 in (1..10)) {
if ($number1 -eq 5) {break}
Write-Host "$number1"
}
write-host "after1"

# Omettre 15. 
foreach ($number2 in (11..20)) {
if ($number2 -eq 15) {continue}
Write-Host "$number2"
}
write-host "after2"

# Annule tout le script à 25, "after3" non imprimé.
foreach ($number3 in (21..30)) {
if ($number3 -eq 25) {return}
Write-Host "$number3"
}
write-host "after3"

11voto

darlove Points 101

Pour arrêter le pipeline dont ForEach-Object fait partie, utilisez simplement l'instruction continue à l'intérieur du bloc de script sous ForEach-Object. continue se comporte différemment lorsque vous l'utilisez dans foreach(...) {...} et dans ForEach-Object {...} et c'est pourquoi c'est possible. Si vous souhaitez continuer à produire des objets dans le pipeline en ignorant certains des objets d'origine, la meilleure méthode pour le faire est de les filtrer en utilisant Where-Object.

11voto

zett42 Points 6849

Il existe un moyen de sortir de ForEach-Object sans générer d'exception. Il utilise une fonctionnalité moins connue de Select-Object, en utilisant le paramètre -First lien, qui en fait interrompt le pipeline une fois que le nombre spécifié d'éléments du pipeline ont été traités.

Exemple simplifié:

$null = 1..5 | ForEach-Object {

    # Faire quelque chose...
    Write-Host $_

    # Évaluer la condition "break" -> sortie $true
    if( $_ -eq 2 ) { $true }  

} | Select-Object -First 1     # Interrompt réellement le pipeline

Sortie:

1
2

Remarquez que l'assignation à $null est là pour masquer la sortie de $true, qui est produite par la condition de sortie. La valeur $true pourrait être remplacée par 42, "sauter", "foobar", à vous de choisir. Nous devons juste envoyer quelque chose à Select-Object pour qu'il interrompe le pipeline.

9voto

marsze Points 751

Étant donné que ForEach-Object est une cmdlet, break et continue auront un comportement différent ici que avec le foreach mot clé. Tous deux arrêteront la boucle mais termineront également l'ensemble du script:

break:

0..3 | foreach {
    if ($_ -eq 2) { break }
    $_
}
echo "Jamais imprimé"

# OUTPUT:
# 0
# 1

continue:

0..3 | foreach {
    if ($_ -eq 2) { continue }
    $_
}
echo "Jamais imprimé"

# OUTPUT:
# 0
# 1

Jusqu'à présent, je n'ai pas trouvé de "bon" moyen d'interrompre un bloc de script foreach sans interrompre le script, sauf en "abusant" des exceptions, bien que powershell core utilise cette approche:

throw:

class CustomStopUpstreamException : Exception {}
try {
    0..3 | foreach {
        if ($_ -eq 2) { throw [CustomStopUpstreamException]::new() }
        $_
    }
} catch [CustomStopUpstreamException] { }
echo "Fin"

# OUTPUT:
# 0
# 1
# Fin

L'alternative (qui n'est pas toujours possible) serait d'utiliser le foreach mot clé:

foreach:

foreach ($_ in (0..3)) {
    if ($_ -eq 2) { break }
    $_
}
echo "Fin"

# OUTPUT:
# 0
# 1
# Fin

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