Подтвердить что ты не робот

Как выйти из объекта ForEach в PowerShell

У меня есть следующий код:

$project.PropertyGroup | Foreach {
    if($_.GetAttribute('Condition').Trim() -eq $propertyGroupConditionName.Trim()) {
        $a = $project.RemoveChild($_);
        Write-Host $_.GetAttribute('Condition')"has been removed.";
    }
};

Вопрос №1: Как выйти из ForEach? Я пробовал использовать "break" и "continue", но он не работает.

Вопрос № 2: я обнаружил, что могу изменить список в цикле foreach... Мы не можем сделать это как на С#... Почему PowerShell позволяет нам это делать?

4b9b3361

Ответ 1

Пункт №1. Ввод break в цикле foreach завершает цикл, но он не останавливает конвейер. Похоже, вы хотите что-то вроде этого:

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

Пункт №2. PowerShell позволяет изменять массив в цикле foreach по этому массиву, но эти изменения не вступают в силу до выхода из цикла. Попробуйте выполнить приведенный ниже код.

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

Я не могу комментировать, почему авторам PowerShell разрешили это, но большинство других языков сценариев (Perl, Python и shell) позволяют создавать аналогичные конструкции.

Ответ 2

Чтобы остановить конвейер, из которого ForEach-Object является частью, просто используйте оператор continue внутри блока script под ForEach-Object. continue ведет себя по-другому, когда вы используете его в foreach(...) {...} и ForEach-Object {...}, и именно поэтому это возможно. Если вы хотите продолжать создавать объекты в конвейере, отбрасывая некоторые из исходных объектов, лучший способ сделать это - отфильтровать с помощью Where-Object.

Ответ 3

Между foreach и foreach-object есть различия.

Очень хорошее описание вы можете найти здесь: MS-ScriptingGuy

Для тестирования в PS у вас есть сценарии, чтобы показать разницу.

ForEach-Object:

# Omit 5.
1..10 | ForEach-Object {
if ($_ -eq 5) {return}
# if ($_ -ge 5) {return} # Omit from 5.
Write-Host $_
}
write-host "after1"
# Cancels whole script at 15, "after2" not printed.
11..20 | ForEach-Object {
if ($_ -eq 15) {continue}
Write-Host $_
}
write-host "after2"
# Cancels whole script at 25, "after3" not printed.
21..30 | ForEach-Object {
if ($_ -eq 25) {break}
Write-Host $_
}
write-host "after3"

Еогеасп

# Ends foreach at 5.
foreach ($number1 in (1..10)) {
if ($number1 -eq 5) {break}
Write-Host "$number1"
}
write-host "after1"
# Omit 15. 
foreach ($number2 in (11..20)) {
if ($number2 -eq 15) {continue}
Write-Host "$number2"
}
write-host "after2"
# Cancels whole script at 25, "after3" not printed.
foreach ($number3 in (21..30)) {
if ($number3 -eq 25) {return}
Write-Host "$number3"
}
write-host "after3"

Ответ 4

Чтобы "продолжить" внутри конвейера ForEach-Object, просто используйте return. Он делает то, что вы ожидаете от continue: пропускает остальную часть блока и продолжает следующую итерацию.

Ответ 5

НЕТ прямого способа вырваться из конвейера, однако вы можете применить фильтры, используя Where-Object. Там, где это возможно, наилучшим решением является преобразование Foreach-Object (где объекты передаются с использованием конвейера) в стандартную циклическую конструкцию Foreach, например:

Foreach-Object (прохождение объектов в конвейере):

1..10 | Foreach-Object {
     if ($_ -eq 3) {return;}
     £_;
}
Write-Host "Only skips one iteration (iterates all 10 times)";

Преобразовано в стандартную циклическую конструкцию Foreach:

Foreach ($i in 1..10) { 
     if ($i -eq 3) {break;}
     $i;
}
Write-Host "Ends the loop after 2nd iteration (iterates only 2 times)";

То же самое с использованием фильтра Where-Object (где преобразование в цикл Foreach невозможно):

1..10 | Where-Object {$_ -le 2} | Foreach-Object {
     $_;
}
Write-Host "Ends the loop after 2nd iteration (iterates only 2 times)";

НТН

Ответ 6

Поскольку ForEach-Object является командлетом, break и continue будут вести себя здесь иначе, чем с ключевым словом foreach keyword. Оба остановят цикл, но также завершат весь сценарий:

перерыв:

0..3 | foreach {
    if ($_ -eq 2) { break }
    $_
}
echo "Never printed"

# OUTPUT:
# 0
# 1

продолжают:

0..3 | foreach {
    if ($_ -eq 2) { continue }
    $_
}
echo "Never printed"

# OUTPUT:
# 0
# 1

До сих пор я не нашел "хорошего" способа разорвать блок сценария foreach без разрыва сценария, кроме "злоупотребляющих" исключений:

бросить:

try {
    0..3 | foreach {
        if ($_ -eq 2) { throw }
        $_
    }
} catch { }
echo "End"

# OUTPUT:
# 0
# 1
# End

Альтернативой (что не всегда возможно) будет использование ключевого слова foreach:

Еогеасп:

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

# OUTPUT:
# 0
# 1
# End