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

This topic contains 8 replies, has 3 voices, and was last updated by Profile photo of Rob Simmers Rob Simmers 2 years, 2 months ago.

  • Author
    Posts
  • #18013
    Profile photo of Johan Hammarstrom
    Johan Hammarstrom
    Participant

    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 🙂

    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

  • #18015
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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)

  • #18018
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    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.

  • #18020
    Profile photo of Johan Hammarstrom
    Johan Hammarstrom
    Participant

    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 🙂

  • #18021
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

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

    @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.

  • #18022
    Profile photo of Johan Hammarstrom
    Johan Hammarstrom
    Participant

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

  • #19176
    Profile photo of Johan Hammarstrom
    Johan Hammarstrom
    Participant

    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 🙂

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

    }
    }

  • #19188
    Profile photo of Johan Hammarstrom
    Johan Hammarstrom
    Participant

    Got it to work 🙂
    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
    }

    }
    }

  • #19206
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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
    

You must be logged in to reply to this topic.