Server Fault is a question and answer site for system and network administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Using Powershell v2.0 I want to delete any files older than X days:

$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;

However, when $backups is empty I get: Remove-Item : Cannot bind argument to parameter 'Path' because it is null.

I've tried:

  1. Protecting the foreach with if (!$backups)
  2. Protecting the Remove-Item with if (Test-Path $file -PathType Leaf)
  3. Protecting the Remove-Item with if ([IO.File]::Exists($file.FullName) -ne $true)

None of these seem to work, what if the recommended way of preventing a foreach loop from being entered if the list is empty?

share|improve this question
@Dan - Tried both ($backups > 0) and (@($backups).count -gt 0), but neither work as expected when there are no files. – SteB Dec 13 '12 at 11:49
up vote 12 down vote accepted

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;



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
share|improve this answer
I've used the first one (it's easier to understand), I couldn't get the second one to work (I was probably doing something wrong). – SteB Dec 13 '12 at 14:18
@SteB You're correct, my example was poorly explained (it still is), but I've provided an edit including your example code. For a better explanation of the behavior, please see this post on Keith Hill's blog, he is not only a PowerShell expert, but also a far better writer than me. Keith is active on StackOverflow, I would encourage you (or anyone interested in PS) to check out his stuff. – jscott Dec 13 '12 at 14:43

I've developed a solution by running the query twice, once to get the files and once to count the files by casting the get-ChilItem to return an array (casting $backups as an array after the fact doesn't seem to work).
At least it works as expected (performance shouldn't be as issue since there'll never be more than a dozen files), if anyone knows of a single-query solution, please post it.

$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;
share|improve this answer
I've edited the post for efficiency as it was easier than explaining in the comments. Just revert it if you don't like it. – Dan Dec 13 '12 at 12:05
@Dan - Doh, can't believe I didn't spot that, thanks. – SteB Dec 13 '12 at 14:08

I know this is an old post but I'd like to point out that the ForEach-Object 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.

share|improve this answer
+1 But with Powershell 3 (circa June 2012) the foreach statement no longer steps into $null, so the error described by OP no longer occurs. See the section "ForEach statement does not iterate over $null" in the Powershell Blog post New V3 Language Features. – jscott Mar 26 '15 at 11:50

Use the following to evaluate if the array has any contents:

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

If the variable doesn't exist Powershell will simply evaluate it as false, so no need to check if it exists.

share|improve this answer
Adding if ($backups.count -gt 0) stops the loop executing even when there's 1 item in $backups. $backups.count even on it's own doesn't output anything. – SteB Dec 13 '12 at 11:30
@SteB Ah, I guess count isn't implemented for whatever object type is holding the data. I scan read and presumed it was an array. – Dan Dec 13 '12 at 11:33
$count = @($backups).count; almost works, but when there are no files if f($count -gt 0) is true! – SteB Dec 13 '12 at 11:47

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.