Remove folders in a directory but keep the three latest (creation date)

Hi admins!

Im very new to powershell but ive heard that this is the place to ask questions/learn powershell.
I have a website where code is deployed to a directory. (d:\websites)
Every time some developer deploys an updated site a new directory is created and the IIS points to this new directory.
d:\websites\ver1
d:\websites\ver2
d:\websites\ver3
d:\websites\ver4
and so on…

I would like to schedule powershell to delete all folders and files in this directory except the last 2 based on creation date. (Because eventually the disk will be filled up)
Understand that you are not supposed to just ask for a complete script here but im just starting to learn powershell :slight_smile:

What i have so far is:

$path = “d:\websites”
$files = Get-ChildItem -Path $path -Recurse | Where-Object { $_.PsIsContainer -eq “$true”}

How would you guys solve this task?

Regards,
Johan

Two cool things Powershell does is Sort easily and also has -WhatIf for testing. If the Powershell version is v3 or higher, you can use -Directory versus the PSIsContainer logic:

$files = Get-ChildItem C:\Websites -Directory | Select Name, CreationTime, FullName | Sort CreationTime

$files

for ($i=0;$i -lt ($files.Count -2);$i++) {
    Remove-Item $files[$i].FullName -WhatIf
}


Name                                      CreationTime                              FullName                                
----                                      ------------                              --------                                
Ver3                                      7/13/2014 9:31:14 AM                      C:\Websites\Ver3                        
Ver1                                      8/14/2014 9:22:50 AM                      C:\Websites\Ver1                        
Ver2                                      8/14/2014 9:22:58 AM                      C:\Websites\Ver2                        
Ver4                                      8/14/2014 9:23:17 AM                      C:\Websites\Ver4                        
Ver5                                      8/14/2014 9:43:40 AM                      C:\Websites\Ver5                        

What if: Performing the operation "Remove Directory" on target "C:\Websites\Ver3".
What if: Performing the operation "Remove Directory" on target "C:\Websites\Ver1".
What if: Performing the operation "Remove Directory" on target "C:\Websites\Ver2".

You can even manipulate the CreationTime of files\folders for amusement:

(Get-Item C:\Websites\Ver5).CreationTime = (Get-Date).AddDays(-54)

This is one of those problems that is quite handily solved with a single pipeline, thanks to PowerShell’s handy generic -Object commands for filtering / sorting / selecting:

Get-ChildItem D:\Websites -Directory |
Sort-Object CreationTime -Descending |
Select-Object -Skip 3 |
Remove-Item -Recurse -Force -WhatIf

If you’re running PowerShell 2.0 and don’t have the -Directory switch on Get-ChildItem, no problem: stick a Where-Object in the pipeline:

Get-ChildItem D:\Websites |
Where-Object { $_.PSIsContainer } |
Sort-Object CreationTime -Descending |
Select-Object -Skip 3 |
Remove-Item -Recurse -Force -WhatIf

(Remove the -WhatIf switch from Remove-Item to make this actually delete things.)

There are times when people go overboard with the “one-liner” approach, but for this purpose, it’s great. It’s a lot like writing a SQL or LINQ query.

Hi Rob!

Seems to work the way i want it but i dont understand some of the variables (($i=0;$i -lt ($files.Count -2);$i++))
Gonna try to look it up.

Many thanks :slight_smile:

Was not aware of the -Skip parameter. Learned something new!! :slight_smile:

@Johan

The for “i” is a standard in most programming\scripting logic. When using notation, i represents integer and s is string and so on. In the above example, you set the integer ($i) to an initial value of 0 which represents the zero index of $files. The next parameter is "run the loop until $i is less than file count minus 2. The typical tough part to grasp is an array starts at 0 and the .Count starts at 1:

Array = 0 | 1 | 2 | 3
Count = 1 | 2 | 3 | 4
$files Ver3 | Ver1 | Ver2 | Ver4

So, when the loop starts it starts at 0 and $files[0] is Ver3 (because I manipulated the CreationTime), file[1] is Ver1 and so on. The last step is what $i increments at, which $i++ is adding 1 each time the loop completes.

Thanks Dave!
Your one-liner was even better for me. ( I understand whats happens :))

Hi !

Im now trying to run this script on multiple remote servers. (psremoting is enabled and firewall is opened)

Get-ChildItem D:\Websites -Directory |
Sort-Object CreationTime -Descending |
Select-Object -Skip 3 |
Remove-Item -Recurse

How would you do this?
What i have so far, which aint working is :slight_smile:

$servers = Get-Content D:\ps\servers.txt
foreach ($serv in $servers) {

Invoke-command $serv {

    $webdir = gci d:\instance\websites\*\* -Directory 

foreach ($wd in $webdir) {
    Get-ChildItem $wd -Directory | 
    Sort-Object CreationTime -Descending | 
    Select-Object -Skip 3 | Remove-Item -Recurse 
}

}
}

Got it to work :slight_smile:
Inputs are welcome if you would do it in some other way.
Thanks!

foreach ($serv in get-content d:\scripts\servers.txt) {

Invoke-command $serv {

    $webdir = gci d:\instance\websites\*\* -Directory 

foreach ($wd in $webdir) {
    Get-ChildItem $wd -Directory | 
    Sort-Object CreationTime -Descending | 
    Select-Object -Skip 3 | Remove-Item -Recurse -Force -WhatIf
}

}
}

Try to get into the habit of indenting your code and I would also recommend adding comments to your ending blocks to know what is ending where:

foreach ($serv in get-content d:\scripts\servers.txt) {
    Invoke-command $serv {
        $webdir = gci d:\instance\websites\*\* -Directory
        foreach ($wd in $webdir) {
            Get-ChildItem $wd -Directory |
            Sort-Object CreationTime -Descending |
            Select-Object -Skip 3 | 
            Remove-Item -Recurse -Force -WhatIf
        } #foreach wd
    } #Invoke-Command
} #foreach server