0

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:

  • group the files by shared named prefix, e.g. FileBam
  • weed out groups that have only 1 element
  • in each remaining, 2+-element group, delete all files except the one with the most recent version.

1 Answer 1

1

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):

  • 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

    • As noted, the -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.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.