Mengapa 'lanjutkan' berperilaku seperti 'putus' di Objek-Foreach?

123

Jika saya melakukan hal berikut dalam skrip PowerShell:

$range = 1..100
ForEach ($_ in $range) {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Saya mendapatkan hasil yang diharapkan dari:

7 is a multiple of 7
14 is a multiple of 7
21 is a multiple of 7
28 is a multiple of 7
35 is a multiple of 7
42 is a multiple of 7
49 is a multiple of 7
56 is a multiple of 7
63 is a multiple of 7
70 is a multiple of 7
77 is a multiple of 7
84 is a multiple of 7
91 is a multiple of 7
98 is a multiple of 7

Namun, jika saya menggunakan pipa dan ForEach-Object, continuetampaknya untuk keluar dari lingkaran pipa.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { continue; }
    Write-Host "$($_) is a multiple of 7"
}

Bisakah saya mendapatkan continueperilaku -seperti saat masih melakukan ForEach-Object, jadi saya tidak perlu memutuskan pipeline saya?

Justin Dearing
sumber
Berikut adalah halaman dengan banyak perintah untuk digunakan dengan foreach: techotopia.com/index.php/…
bgmCoder
Menemukan penjelasan dan sampel yang layak di sini ... powershell.com/cs/blogs/tips/archive/2015/04/27/…
Nathan Hartley

Jawaban:

164

Cukup gunakan sebagai returnpengganti continue. Ini returnkembali dari blok skrip yang dipanggil oleh ForEach-Objectpada iterasi tertentu, dengan demikian, ini mensimulasikan continuein a loop.

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) { return }
    Write-Host "$($_) is a multiple of 7"
}

Ada hal yang harus diingat saat melakukan refactoring. Kadang-kadang seseorang ingin mengubah foreachblok pernyataan menjadi pipa dengan ForEach-Objectcmdlet (bahkan memiliki alias foreachyang membantu membuat konversi ini mudah dan membuat kesalahan juga mudah). Semua continues harus diganti dengan return.

PS: Sayangnya, hal ini tidak mudah untuk mensimulasikan breakdi ForEach-Object.

Roman Kuzmin
sumber
2
Dari apa yang dikatakan OP ternyata continuedapat digunakan untuk mensimulasikan breakdalam ForEach-Object:)
Richard Hauer
6
@ Richard Hauer A seperti itu continueakan merusak seluruh naskah, tidak hanya di ForEach-Objectmana itu digunakan.
Roman Kuzmin
22

Karena For-Eachobjek adalah cmdlet dan tidak loop dan continuedan breaktidak berlaku untuk itu.

Misalnya, jika Anda memiliki:

$b = 1,2,3

foreach($a in $b) {

    $a | foreach { if ($_ -eq 2) {continue;} else {Write-Host $_} }

    Write-Host  "after"
}

Anda akan mendapatkan keluaran sebagai:

1
after
3
after

Itu karena continueakan diterapkan ke loop foreach luar dan bukan cmdlet foreach-object. Dengan tidak adanya lingkaran, tingkat terluar, sehingga memberi Anda kesan seperti itu break.

Jadi, bagaimana Anda mendapatkan continueperilaku seperti? Salah satunya tentu saja Where-Object :

1..100 | ?{ $_ % 7  -eq 0} | %{Write-Host $_ is a multiple of 7}
manojlds
sumber
Menggunakan cmdlet Where-Object adalah saran yang bagus. Dalam kasus saya yang sebenarnya, menurut saya tidak masuk akal untuk membuat beberapa baris kode sebelum pernyataan if saya menjadi satu baris panjang kode yang sulit dibaca. Namun, itu akan berhasil untuk saya dalam situasi lain.
Justin Dearing
@JustinDearing - In my actual case, I don't think it makes sense to make the multiple lines of code preceding my if statement into a single long line of hard to read code.Apa maksudmu?
manojlds
3
@manojlds mungkin dia berpikir bahwa solusi satu baris Anda "sulit dibaca", setidaknya bagi saya sangat sebaliknya. Cara pipeline untuk melakukan sesuatu sangat kuat dan jelas dan merupakan pendekatan yang tepat untuk hal-hal sederhana seperti itu. Menulis kode di shell tanpa memanfaatkannya tidak ada gunanya.
mjsr
Dalam kasus saya ini adalah jawaban yang benar, tambahkan kondisi where untuk menyaring objek yang akan saya lanjutkan atau kembalikan sehingga saya tidak perlu memprosesnya terlebih dahulu. +1
Chris Magnuson
3

Alternatif lain adalah semacam peretasan, tetapi Anda dapat membungkus blok Anda dalam satu lingkaran yang akan dieksekusi sekali. Dengan begitu, continueakan ada efek yang diinginkan:

1..100 | ForEach-Object {
    for ($cont=$true; $cont; $cont=$false) {
        if ($_ % 7 -ne 0 ) { continue; }
        Write-Host "$($_) is a multiple of 7"
    }
}
zdan
sumber
4
Sejujurnya, itu jelek :) Dan bukan hanya hack karena sebagai ganti foreach-object, Anda juga bisa menggunakan foreach loop.
manojlds
1
@manojlds: 1..100 hanya untuk ilustrasi. do {} while ($ False) bekerja sebaik for loop dan sedikit lebih intuitif.
Harry Martyrossian
2

elsePernyataan sederhana membuatnya berfungsi seperti di:

1..100 | ForEach-Object {
    if ($_ % 7 -ne 0 ) {
        # Do nothing
    } else {
        Write-Host "$($_) is a multiple of 7"
    }
}

Atau dalam satu pipa:

1..100 | ForEach-Object { if ($_ % 7 -ne 0 ) {} else {Write-Host "$($_) is a multiple of 7"}}

Namun solusi yang lebih elegan adalah membalik pengujian Anda dan menghasilkan output hanya untuk kesuksesan Anda

1..100 | ForEach-Object {if ($_ % 7 -eq 0 ) {Write-Host "$($_) is a multiple of 7"}}
Alvin
sumber