Preface:
This is a self-answered question, which is why there's no solution attempt in the question itself.
The task description is a focused reformulation of this now-deleted question.
I have a large set of files whose names contain version numbers; e.g.:
FileBam-0.10.1.zip
FileBam-0.2.0.zip
FileBoozle-1.7.2.zip
FileBoozle-1.7.5.zip
FileBoozle-1.7.10.zip
FileWam-0.97.40.zip
OtherFile-5.5.zip
Using PowerShell, I'd like to:
FileBam
Use the following:
Get-ChildItem -Filter *.zip |
Group-Object -Property { ($_.BaseName -split '-')[0] } |
Where-Object Count -gt 1 |
ForEach-Object {
$_.Group |
Sort-Object -Descending -Property { [version] ($_.BaseName -split '-')[-1] } |
Select-Object -Skip 1
} |
Remove-Item -WhatIf
Note: The -WhatIf
common parameter in the command above previews the operation. Remove -WhatIf
and re-execute once you're sure the operation will do what you want.
Get-ChildItem
is used to retrieve the list of input files, targeting files with filename extension .zip
in the current directory (use -LiteralPath
to target a different directory):
[System.IO.FileInfo]
instances and are sent through the pipeline (|
) one by one.Group-Object
is used to group the input files by the first -
-separated token in their base name:
The script block ({ ... }
) acts as the implied Expression
entry of a calculated property and specifies the grouping criterion.
In it, the automatic $_
variable refers to the input file-info at hand.
The .BaseName
property contains the base file name (the file name without the extension) and its value is split into an array of tokens by -
, via the -split
operator, followed by extraction of the 1st token ([0]
).
Where-Object
is used to filter the groups by whether they contain more than 1 (2 or more) elements (files), by way of testing whether the Count
property value of the [Microsoft.PowerShell.Commands.GroupInfo]
instances output by Group-Object
, using the -gt
(greater-than) operator and simplified syntax.
ForEach-Object
is used to process each 2+-element group:
Again, the automatic $_
variable refers to the input object at hand, which is a [Microsoft.PowerShell.Commands.GroupInfo]
in this case, whose .Group
property contains the group's array of elements. Sending that array to the pipeline (|
) auto-enumerates the elements, i.e. sends them one by one.
Similar to the Group-Object
call above, a script block acting as a calculated property is used as the sort criterion passed to Sort-Object
, along with requesting descending sort order (-Descending
), i.e. from highest to lowest version:
This time, it is the last ([-1]
) token that is split off from the file base name, i.e. the version number string.
Said string is cast to type [version]
([System.Version]
), resulting in an object that properly performs component-by-component comparisons when being sorted.
Select-Object
is then used to skip the 1st result object (-Skip 1
), which is the file with the highest version number in the group.
The remaining objects sent through the pipeline are therefore the files to delete, and their deletion is performed via Remove-Item
-WhatIf
common parameter in the command previews the operation, i.e. tells you which files would be deleted, giving you the option to double-check whether the logic works as intended. Removing -WhatIf
will perform actual deletion.