Dustree
Dustree

Reputation: 3

Deleting folders that have serialized names in Powershell

My problem may\may not be complicated but I have been scratching my head and searching for a way to do this but so far have not come up with much.

I have a folder structure like so:

C:\
└───ParentFolder
    ├───ChildFolder1
    │   ├───SubFolderA_1
    │   ├───SubFolderA_2
    │   ├───SubFolderA_3
    │   ├───SubFolderA_4
    │   ├───SubFolderB_1
    │   ├───SubFolderB_2
    │   ├───SubFolderB_3
    │   └───SubFolderB_4
    └───ChildFolder2
        ├───SubFolderA_1
        ├───SubFolderA_2
        ├───SubFolderA_3
        ├───SubFolderA_4
        ├───SubFolderB_1
        ├───SubFolderB_2
        ├───SubFolderB_3
        └───SubFolderB_4

What I'm looking for is a PowerShell script that would utilize the serialized nature of the "SubFolders" names to delete older versions, leaving only the most recent SubFolders in place.

Using the example above, this would mean the script would delete SubFolderA_1 to SubFolderA_3 and SubFolderB_1 to SubFolderB_3, leaving only SubFolderA_4 and SubfolderB_4 in the ChildFolders.

Would anyone know a way of doing this? I was thinking about Object Sorting + The Recursive Function + pattern matching, but I don't seem to get anywhere with it. I'm a PS noob by the way.

Your help would be much appreciated.

Upvotes: 0

Views: 185

Answers (2)

user6811411
user6811411

Reputation:

Provided there is only one underscore,

  • list folders with multiple wildcards
  • split the fullname at the underscore and
  • Group by the first part,
  • iterate the groups,
  • sort descending (if there may be diversing places convert to an int or use $ToNatural) and
  • skip the first
  • remove remaining folders.

This is quite similar to Lee_Daileys script but will sort with all numbers padded left with zeros to a unique width.

## $ToNatural from Roman Kuzmin source https://stackoverflow.com/a/5429048/6811411
$ToNatural = { [regex]::Replace($_, '\d+', { $args[0].Value.PadLeft(20,"0") }) }

Get-ChildItem C:\ParentFolder\ChildFolder*\Subfolder* | 
  Group-Object @{e={$_.fullname.split('_')[0]}} | ForEach-Object {
    $_.Group | Sort $ToNatural -Descending | Select -Skip 1 | Remove-Item -WhatIf
  }

If the output looks OK, remove the trailing -WhatIf

Upvotes: 0

Lee_Dailey
Lee_Dailey

Reputation: 7479

here's one way to do it. [grin] the heart of it is the Group-Object cmdlet. one thing often overlooked about it is the ability to use a calculated property much as can be done with the Select-Object cmdlet.

# fake reading in a list of directories
#    in real life, use Get-ChildItem -Directory
$DirList = @(
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderA_1'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderA_2'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderA_3'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderA_4'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderB_1'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderB_2'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderB_3'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder1\SubFolderB_4'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderA_1'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderA_2'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderA_3'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderA_4'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderB_11'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderB_22'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderB_3'
    [System.IO.DirectoryInfo]'C:\ParentFolder\ChildFolder2\SubFolderB_44'
)

$GroupedDirList = $DirList |
    # changed from sorting by the FullName to sorting by the trailing number
    #    thanks to LotPings for pointing out the glitch with multi-digit numbers
    Sort-Object {[int]$_.FullName.Split('_')[1]} |
    Group-Object {$_.FullName.Split('_')[0]}

foreach ($GDL_Item in $GroupedDirList)
    {
    $GDL_Item.Group |
        Select-Object -SkipLast 1 |
        ForEach-Object {
            # remove the quotes, the Write-Host, and the "$()" when you do this for real
            # can't use the "-WhatIf" parameter here since the dirs don't actually exist on my system
            Write-Host "Remove-Item -LiteralPath $($_.FullName) -Recurse -WhatIf"
            }

    '=' * 20
    }

output ...

Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderA_1 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderA_2 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderA_3 -Recurse -WhatIf
====================
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderB_1 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderB_2 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder1\SubFolderB_3 -Recurse -WhatIf
====================
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderA_1 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderA_2 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderA_3 -Recurse -WhatIf
====================
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderB_3 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderB_11 -Recurse -WhatIf
Remove-Item -LiteralPath C:\ParentFolder\ChildFolder2\SubFolderB_22 -Recurse -WhatIf
====================

Upvotes: 1

Related Questions