0

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.

Dustree
  • 3
  • 1
  • Hello, it's nice that You have Joined StackOverflow community. When asking questions - please follow instructions from below link so that we can help You effectively. Please let us know what dud You try so far, and we will help You build on that. https://stackoverflow.com/help/how-to-ask – Tomek Jan 04 '19 at 08:34

2 Answers2

1

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
====================
Lee_Dailey
  • 7,292
  • 2
  • 22
  • 26
  • 1
    Nice one(+1) was about to post a similar script. But what happens if the trailing count exceeds 9/one place ? –  Jan 02 '19 at 14:08
  • @LotPings - hah! very good point ... i forgot that `Sort-Object` would be doing a string sort without any awareness of numbers. [*blush*] i'm off to fix that ... – Lee_Dailey Jan 02 '19 at 14:19
  • 1
    Many thanks Lee_Dailey. I have also tried your script and this works perfectly with the addition of "-recurse -force -confirm:$false". to remove Item. Thanks again. – Dustree Jan 02 '19 at 17:17
  • @Dustree - you are most welcome! glad to have helped a little ... [*grin*] – Lee_Dailey Jan 02 '19 at 17:44
0

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

  • 1
    Much appreciated LotPings, this works extremely well. I just needed to add "-recurse -force -confirm:$false" to the Remove-Item function to get around the constantly presented prompt "The item at has children and the Recurse parameter was not specified. If you continue, all children will be removed with the item. Are you sure you want to continue? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):" – Dustree Jan 02 '19 at 16:18