Protect foreach loop when empty list

Solution 1:

With Powershell 3 the foreach statement does not iterate over $null and the issue described by OP no longer occurs.

From the Windows PowerShell Blog post New V3 Language Features:

ForEach statement does not iterate over $null

In PowerShell V2.0, people were often surprised by:

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

got here

This situation often comes up when a cmdlet doesn’t return any objects. In PowerShell V3.0, you don’t need to add an if statement to avoid iterating over $null. We take care of that for you.

For PowerShell $PSVersionTable.PSVersion.Major -le 2 see the following for original answer.


You have two options, I mostly use the second.

Check $backups for not $null. A simple If around the loop can check for not $null

if ( $backups -ne $null ) {

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

}

Or

Initialize $backups as a null array. This avoids the ambiguity of the "iterate empty array" issue you asked about in your last question.

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

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

Sorry, I neglected to provide an example integrating your code. Note the Get-ChildItem cmdlet wrapped in the array. This would also work with functions which could return a $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
}

Solution 2:

I know this is an old post but I'd like to point out that the ForEach-Objec cmdlet doesn't suffer the same issue as using the ForEach key word. So you can pipe the results of DIR to ForEach and just reference the file using $_, like:

$backups | ForEach{ Remove-Item $_ }

You can actually forward the Dir command itself through the pipe and avoid even assigning the variable like:

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

I added line breaks for readability.

I understand some people like ForEach/In for readability. Sometimes ForEach-Object can get a little hairy, especially if you are nesting as it gets hard to follow the $_ reference. At any rate, for a small operation like this it's perfect. Many people also assert it's faster however I've found that to be only slightly.

Tags:

Powershell