4

I have a file that i backup daily, as sample like this :

Before deleting all but the latest :

Folder C:\Backup\
File : 
Backup1-Database-A.bak | 1/10/2020 1:24 PM
Backup2-Database-A.bak | 2/10/2020 1:24 PM
Backup1-Database-B.bak | 1/10/2020 1:24 PM
Backup2-Database-B.bak | 2/10/2020 1:24 PM
Backup2-Database-B.bak | 3/10/2020 1:24 PM

Desired state after deleting all but the latest :

Folder C:\Backup\
File : 
Backup2-Database-A.bak | 2/10/2020 1:24 PM
Backup2-Database-B.bak | 3/10/2020 1:24 PM

How can I keep the latest file for each database?

I'm using this script, but it deletes all .bak so the backup of database A is gone, but I want to keep the latest for each database.

$mydays = (Get-Date).AddDays(-7)
$path = "C:\Backup\"
Get-Childitem -path $path -recurse -force | Where-Object { !$_.PSIsContainer -and $_.name -like "*.bak" -and $_.LastWriteTime -lt $mydays} | Remove-Item -Force
mklement0
  • 382,024
  • 64
  • 607
  • 775
lass
  • 149
  • 1
  • 7
  • If you just want that last (one) days worth then try `$mydays = (Get-Date).AddDays(-1)`. – jfrmilner Oct 04 '20 at 13:05
  • @jfrmilner yes, i tried -1 . but it delete all, and not keep the latest file, maybe i wrong at name -like script – lass Oct 04 '20 at 13:20
  • 2
    instead of testing for a certain date ... just sort by date and keep only the newest one. [*grin*] – Lee_Dailey Oct 04 '20 at 13:33
  • 1
    As aside, it is more efficient to use `-Filter '*.bak'` than `$_.name -like "*.bak"`. and if your PowerShell version is 3.0 or newer, use switch `-File` instead of `!$_.PSIsContainer` – Theo Oct 04 '20 at 14:05
  • @Lee_Dailey yes, thankyou. but how i can skip the all LastWriteTime, because it will delete all and i want to keep the 2 file A & B in the LastWriteTime – lass Oct 04 '20 at 17:01
  • 1
    @lass - it looks like mklement0 has covered that in his Answer with the `Group-Object` command. [*grin*] – Lee_Dailey Oct 04 '20 at 19:41

1 Answers1

2

As Lee_Daily suggests, rather than using a time window to filter by, it is simpler to sort your files by last write time and delete all but the latest files.

The challenge is to determine which files relate to the same database and should therefore be considered as a group, so that you keep the most recent backup in each group.

The code below uses the following approach:

  • The Group-Object cmdlet allows you to group the input files by the shared part of the file names that indicates the database.

    • Passing script block { $_.Name -replace '^Backup\d+-' } to Group-Object uses the regex-based -replace operator to make it group the input file objects by the shared name part that indicates the source database, by removing the 'Backup<n>-' prefix.
    • E.g., the result for both 'Backup1-Database-A.bak' and 'Backup2-Database-A.bak' is just 'Database-A.bak', so the two files are grouped together.
  • The files in each resulting group can then be sorted by last write time in descending order, with
    Sort-Object.

  • You can exclude the first - and therefore most recent - file from the output with Select-Object -Skip 1.

  • The resulting files - all but the most recent ones for each database - can then be sent to Remove-Item.

$path = 'C:\Backup'
Get-Childitem -Path $path -Recurse -Force -File -Filter *.bak | 
  Group-Object { $_.Name -replace '^Backup\d+-' } | 
    ForEach-Object { 
      $_.Group | Sort-Object LastWriteTime -Descending | Select-Object -Skip 1 |
        Remove-Item -Force -Whatif  
    }

Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.

Also note the simplification of the initial file selection: -File (PSv3+) limits output to files only (not also directories), and -Filter *.bak is an efficient way to filter by filename extension.

mklement0
  • 382,024
  • 64
  • 607
  • 775