Boş liste olduğunda foreach döngüsünü koru


10

Powershell v2.0'ı kullanma X günden eski dosyaları silmek istiyorum:

$backups = Get-ChildItem -Path $Backuppath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like "backup*")}

foreach ($file in $backups)
{
    Remove-Item $file.FullName;
}

Ancak, $ backups boş olduğunda: Remove-Item : Cannot bind argument to parameter 'Path' because it is null.

Denedim:

  1. Önsöz ile koruma if (!$backups)
  2. Remove-Item ile Koruma if (Test-Path $file -PathType Leaf)
  3. Remove-Item ile Koruma if ([IO.File]::Exists($file.FullName) -ne $true)

Bunların hiçbiri işe yaramıyor gibi görünüyorsa, liste boşsa bir foreach döngüsünün girilmesini önlemenin önerilen yolu ne olur?


@Dan - Hem ($ backups> 0) hem de (@ ($ backups) .count -gt 0) öğesini denedi, ancak dosya olmadığında ikisi de beklendiği gibi çalışmaz.
SteB

Yanıtlar:


19

Powershell 3 ile foreachifade yinelenmez $nullve OP tarafından açıklanan sorun artık oluşmaz.

Gönderen Windows PowerShell blog yazı Yeni V3 Dil Özellikleri :

ForEach deyimi $ null değerinin üzerinde yineleme yapmıyor

PowerShell V2.0'da insanlar genellikle şunlara şaşırdılar:

PS> foreach ($i in $null) { 'got here' }

got here

Bu durum genellikle bir cmdlet herhangi bir nesne döndürmediğinde ortaya çıkar. PowerShell V3.0'da, $ null değerini yinelemekten kaçınmak için bir if ifadesi eklemenize gerek yoktur. Sizin için bununla ilgileniyoruz.

PowerShell $PSVersionTable.PSVersion.Major -le 2için orijinal yanıt için aşağıdakilere bakın.


İki seçeneğiniz var, çoğunlukla ikincisini kullanıyorum.

Kontrol $backupsiçin değil $null. IfDöngünün etrafındaki basit bir$null

if ( $backups -ne $null ) {

    foreach ($file in $backups) {
        Remove-Item $file.FullName;
    }

}

Veya

$backupsBoş bir dizi olarak başlat . Bu , son sorunuzda sorduğunuz "yinelenen boş dizi" sorununun belirsizliğini önler .

$backups = @()
# $backups is now a null value array

foreach ( $file in $backups ) {
    # this is not reached.
    Remove-Item $file.FullName
}

Üzgünüz, kodunuzu entegre eden bir örnek vermeyi ihmal ettim. Get-ChildItemDiziye sarılmış cmdlet'i not edin . Bu aynı zamanda dönebilen işlevlerle de çalışır $null.

$backups = @(
    Get-ChildItem -Path $Backuppath |
        Where-Object { ($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like "backup*") }
)

foreach ($file in $backups) {
    Remove-Item $file.FullName
}

Birincisini kullandım (anlaması daha kolay), ikincisini çalıştıramadım (muhtemelen yanlış bir şey yapıyordum).
SteB

@SteB Haklısınız, örneğim zayıf bir şekilde açıklanmış (hala açık), ancak örnek kodunuzu içeren bir düzenleme sağladım. Davranışın daha iyi bir açıklaması için lütfen Keith Hill'in blogundaki bu gönderiye bakın , o sadece bir PowerShell uzmanı değil, aynı zamanda benden çok daha iyi bir yazar. Keith StackOverflow'da aktif, sizi (veya PS ile ilgilenen herhangi birini) eşyalarını kontrol etmeye teşvik ediyorum.
jscott

2

Bu eski bir yazı olduğunu biliyorum ama ForEach-Object cmdlet ForEach anahtar sözcüğü kullanarak aynı sorunu muzdarip olduğunu belirtmek istiyorum. Böylece DIR sonuçlarını ForEach'a yönlendirebilir ve sadece $ _ kullanarak dosyaya başvurabilirsiniz, örneğin:

$backups | ForEach{ Remove-Item $_ }

Dir komutunun kendisini kanaldan iletebilir ve değişkeni atamaktan bile kaçınabilirsiniz:

Get-ChildItem -Path $Backuppath | 
Where-Object {
             ($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and `
             (-not $_.PSIsContainer) -and ($_.Name -like "backup*")
             } |
ForEach{ Remove-Item $_ }

Okunabilirlik için satır sonları ekledim.

Okunabilirlik için ForEach / In gibi bazı insanları anlıyorum. Bazen ForEach nesnesi, özellikle $ _ referansını takip etmek zorlaştıkça iç içe geçiyorsanız biraz tüylü olabilir. Her halükarda, böyle küçük bir operasyon için mükemmeldir. Birçok insan bunun daha hızlı olduğunu iddia ediyor, ancak bunun sadece biraz olduğunu buldum.


+1 Ancak Powershell 3 ile (Haziran 2012 civarında) foreachifade artık devreye girmez $null, bu nedenle OP tarafından açıklanan hata artık oluşmaz. Powershell Blog yazısı Yeni V3 Dil Özellikleri'nde "ForEach deyimi $ null üzerinde yineleme yapmıyor" bölümüne bakın .
jscott

1

Ben bir kez dosyaları almak için bir kez ve bir dizi dönmek için get-ChilItem döküm yoluyla dosyaları saymak için iki kez sorgusu çalıştırarak bir çözüm geliştirdim (gerçeği çalışmıyor gibi sonra $ yedekleri bir dizi olarak döküm) .
En azından beklendiği gibi çalışır (bir düzineden fazla dosya olmayacağından performans sorun olmamalıdır), eğer birisi tek sorgu çözümünü biliyorsa, lütfen gönderin.

$count = @(Get-ChildItem -Path $zipFilepath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like $partial + "*")}).count;

if ($count -gt 0)
{
    $backups = Get-ChildItem -Path $zipFilepath | 
                Where-Object {($_.lastwritetime -lt (Get-Date).addDays(-$DaysKeep)) -and (-not $_.PSIsContainer) -and ($_.Name -like $partial + "*")};

    foreach ($file in $backups)
    {
        Remove-Item $file.FullName;
    }
}

1
Yorumlarda açıklamaktan daha kolay olduğu için yazıyı verimlilik için düzenledim. Hoşunuza gitmiyorsa geri alın.
Dan

@Dan - Doh, bunu fark etmediğime inanamıyorum, teşekkürler.
SteB

0

Dizinin herhangi bir içeriği olup olmadığını değerlendirmek için aşağıdakileri kullanın:

if($backups.count -gt 0) { echo "Array has contents" } else { echo "Array is empty" }

Değişken yoksa Powershell bunu yanlış olarak değerlendirir, bu yüzden var olup olmadığını kontrol etmeye gerek yoktur.


($ Backups.count -gt 0) eklenirse, $ backups öğesinde 1 öğe olsa bile döngü yürütülmesini durdurur. $ backups.count kendi başına bile hiçbir çıktı vermez.
SteB

@SteB Ah, sanırım sayım veriyi tutan nesne türü için uygulanmadı. Tarama yaptım ve bir dizi olduğunu varsaydım.
Dan

$ count = @ ($ yedekler) .count; neredeyse çalışır, ancak f ($ count -gt 0) doğruysa dosya olmadığında!
SteB
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.