Powershell Delete all files apart from the latest file per day - powershell

I have a folder that contains a lot of files, multiple files per day.
I would like to script something that deletes all but the latest file per day.
I have seen a lot of scripts that delete files over X days old but this is slightly different and having written no powershell before yesterday (I'm exclusively tsql), I'm not really sure how to go about it.
I'm not asking anyone to write the code for me but maybe describe the methods of achieving this would be good and I can go off an research how to put it into practise.
All files are in a single directory, no subfolders. there are files I dont want to delete, the files i want to delete have file name in format constant_filename_prefix_YYYYMMDDHHMMSS.zip
Is powershell the right tool? Should i instead be looking at Python (which I also don't know) Powershell is more convinient since other code we have is written in PS.

PowerShell has easy to use cmdlets for this kind of thing.
The question to me is if you want the use the dates in the file names, or the actual LastWriteTime dates of the files themselves (as shown in File Explorer).
Below two ways of handling this. I've put in a lot of code comments to help you get the picture.
If you want to remove the files based on their actual last write times:
$sourceFolder = 'D:\test' # put the path to the folder where your files are here
$filePrefix = 'constant_filename_prefix'
Get-ChildItem -Path $sourceFolder -Filter "$filePrefix*.zip" -File | # get files that start with the prefix and have the extension '.zip'
Where-Object { $_.BaseName -match '_\d{14}$' } | # that end with an underscore followed by 14 digits
Sort-Object -Property LastWriteTime -Descending | # sort on the LastWriteTime property
Select-Object -Skip 1 | # select them all except for the first (most recent) one
Remove-Item -Force -WhatIf # delete these files
OR
If you want to remove the files based the dates in the file names.
Because the date formats you used are sortable, you can safely sort on the last 14 digits of the file BaseName:
$sourceFolder = 'D:\test'
$filePrefix = 'constant_filename_prefix'
Get-ChildItem -Path $sourceFolder -Filter "$filePrefix*.zip" -File | # get files that start with the prefix and have the extension '.zip'
Where-Object { $_.BaseName -match '_\d{14}$' } | # that end with an underscore followed by 14 digits
Sort-Object -Property #{Expression = {$_.BaseName.Substring(14)}} -Descending | # sort on the last 14 digits descending
Select-Object -Skip 1 | # select them all except for the first (most recent) one
Remove-Item -Force -WhatIf # delete these files
In both alternatives you will find there is a switch -WhatIf at the end of the Remove-Item cmdlet. Yhis is for testing the code and no files wil actually be deleted. Instead, with this switch, in the console it writes out what would happen.
Once you are satisfied with this output, you can remove or comment out the -WhatIf switch to have the code delete the files.
Update
As I now understand, there are multiple files for several days in that folder and you want to keep the newest file for each day, deleting the others.
In that case, we have to create 'day' groups of the files and withing every group sort by date and delete the old files.
This is where the Group-Object comes in.
Method 1) using the LastWriteTime property of the files
$sourceFolder = 'D:\test' # put the path to the folder where your files are here
$filePrefix = 'constant_filename_prefix'
Get-ChildItem -Path $sourceFolder -Filter "$filePrefix*.zip" -File | # get files that start with the prefix and have the extension '.zip'
Where-Object { $_.BaseName -match '_\d{14}$' } | # that end with an underscore followed by 14 digits
Group-Object -Property #{Expression = { $_.LastWriteTime.Date }} | # create groups based on the date part without time part
ForEach-Object {
$_.Group |
Sort-Object -Property LastWriteTime -Descending | # sort on the LastWriteTime property
Select-Object -Skip 1 | # select them all except for the first (most recent) one
Remove-Item -Force -WhatIf # delete these files
}
Method 2) using the date taken from the file names:
$sourceFolder = 'D:\test' # put the path to the folder where your files are here
$filePrefix = 'constant_filename_prefix'
Get-ChildItem -Path $sourceFolder -Filter "$filePrefix*.zip" -File | # get files that start with the prefix and have the extension '.zip'
Where-Object { $_.BaseName -match '_\d{14}$' } | # that end with an underscore followed by 14 digits
Group-Object -Property #{Expression = { ($_.BaseName -split '_')[-1].Substring(0,8)}} | # create groups based on the date part without time part
ForEach-Object {
$_.Group |
Sort-Object -Property #{Expression = {$_.BaseName.Substring(14)}} -Descending | # sort on the last 14 digits descending
Select-Object -Skip 1 | # select them all except for the first (most recent) one
Remove-Item -Force -WhatIf # delete these files
}

Related

Powershell - Selective moving files into subfolder (keeping the newest of each FIRST13 letter grouping)

extreme powershell newbie here so please be gentle...
I have a filing system where where files in folders are generated semi-automatically, with multiple versions being kept as redundancy (we really do revert regularly).
Files within the folder are named with the first 13 characters as the identifier, with various dates or initials afterwards.
12345-A-01-01_XYZ_20191026.pdf
i.e. the file is 12345-A-01-01 and everything past the first 13 characters is "unpredictable"
FILE000000001xxxxxxx.pdf
FILE000000001yyyy.pdf
FILE000000001zzzzzz.pdf
FILE000000002xxxx.pdf
FILE000000002yyy.pdf
FILE000000002zz.pdf
FILE000000003xx.pdf
FILE000000003yyy.pdf
FILE000000003zzzz.pdf
I'm trying to write a script that can determine the newest version (by date modified file property) of each file "group"
i.e. the newest FILE000000001*.pdf etc
and slide all the others into the .\Superseded subfolder
All I've managed to get so far is a "list" sorting to show the newest at the top of "each" group... now I need to know how to keep that file, and move the others... any direction or help would be great thanks :)....
$_SourcePath = "C:\testfiles"
$_DestinationPath = "C:\testfiles\Superseded"
Get-ChildItem $_SourcePath |
Where-Object {-not $_.PSIsContainer} |
Group-Object { $_.Basename.Substring(0,12) } |
foreach {
$_.Group |
sort LastWriteTime -Descending
} | Move-Item -Destination $_DestinationPath
I think you are pretty close. Since you sorted descending order you should just skip the first file:
$SourcePath = "C:\testfiles"
$DestinationPath = "C:\testfiles\Superseded"
Get-ChildItem $SourcePath -File |
Group-Object { $_.Basename.Substring(0,12) } |
ForEach-Object {
$_.Group |
Sort-Object LastWriteTime -Descending |
Select-Object -skip 1 |
Move-Item -Destination $DestinationPath -WhatIf
# Note: Above, the move has to be in each iteration of the loop
# so we skip the first (newest) of each file.
}
You don't need Where-Object {-not $_.PSIsContainer} , use the -File Parameter instead.
Also I wouldn't name your variables $_***. That's bound to get confused with $_ like the pipeline variable.
I added -WhatIf to the move command so you can test without causing any damage ...
I didn't test it, but it looks about right.

Check file names and delete files with lower number in suffix

I have a following task on PowerShell:
I need to check files on remote machines:
For instance:
Get-ChildItem \\ServerName\data\
In this folder I have following files:
standard_file.0.tst
standard_file.1.tst
standard_file.2.tst
standard_file.3.tst
So, i need to delete files with lower number prefix (based on file name).
In the end, into the folder should be only one file with biggest prefix.
For instance:
standard_file.3.tst
I broke up my mind - and have no any ideas how to perform this.
Could you please push me to the right direction?
Thanks in advance.
You could use a regex to get the number and cast it to an int. Then sort the filenames by the number using the Sort-Object cmdlet so the file with the highest number will be the last. Then you select all objects using Select-Object and skip the last one and finally remove it using Remove-Item:
Get-ChildItem '\ServerName\data\' |
Sort-Object { [int][regex]::Match($_, '.*?(\d+)\.[^.]+$').Groups[1].Value } |
Select-Object -SkipLast 1 |
Remove-Item
Regex:
.*?(\d+)\.[^.]+$
This will gather all the files in the path that have numerical suffixes in their names. The way that is done is by using a regex to match all of the digits on the end of the basename. Sorting on the result of that match in descending order will put the wanted file on the top of the list. We then remove the remaining files by skipping that first result.
$path = "c:\temp"
Get-ChildItem $path | Where-Object{$_.BaseName -match "\.\d+$"} |
Sort-Object -Property {$_.BaseName -match "\.(\d+)$";[int]$Matches[1]} -Descending |
Select-Object -Skip 1 |
Remove-Item -Confirm:$false -WhatIf
Remove -WhatIf when you are sure that it will remove the files you want.

Outputing Remove-Item to a log file

Scanning a directory for a specific set of files and sorting them by date. Keeping 7 of the LATEST copies of the file regardless of date, and removing the oldest if over 7. I am having a hard time producing a log file showing the deletes since Remove-Item has no output.
Below is a copy of my code:
$path = "C:\- Deploy to Production -\Previous Deploys\*_*_BOAWeb.rar" #BOA
$files = Get-ChildItem -Path $path -Recurse | Where-Object {-not $_.PsIsContainer}
$keep = 7
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime |
Select-Object -First ($files.Count - $keep) |
Remove-Item -Force
}
First off you are over complicating things. Add -Descending to your Sort command, and then change your Select to -Skip $keep. It's simpler that way. Then you have options for outputting your deleted files.
Remove-Item -Force -Verbose 4>&1 | Add-Content C:\Path\To\DeletedFiles.log
or (keeping with your current code above)
Select-Object -First ($files.Count - $keep) |Tee-Object -filepath C:\Path\To\DeletedFiles.log -append
The first will output the verbose output of Delete-Item and append it to whatever log file you specify the path for (use Set-Content if you want to replace the log instead). The second option will append the [FileInfo] objects onto a log that you specify.
Edit: As pointed out by Ansgar Wiechers, I had forgotten to to combine my verbose and stdout streams, so 4>&1 was added to the above code to correct that issue.

Keep x versions of a file in Folder - delete rest

I have backup process that makes a copy of files and appending system time at the end. Date stamp indicates when the file was received via FTP (ddMMYYYYhhmmss).
fileName1.ZIP02062015090653
fileName1.ZIP01062015090653
fileName1.ZIP31052015090653
fileName1.ZIP29052015090653
fileName1.ZIP28052015090653
fileName1.ZIP21052015090653
fileName2.ZIP02062015090653
fileName3.ZIP02062015090653
reportName1.PDF02062015090653
reportNameX.TXT02062015090653
etc..
I need the script to keep the 5 most recent versions of each file.
i.e. fileName1.ZIP21052015090653 <- this should get deleted.
I was trying to work of a script below but it deletes everything after 5th file..
gci C:\temp\ -Recurse| where{-not $_.PsIsContainer}| sort CreationTime -desc|
select -Skip 5| Remove-Item -Force
I don't mind if script uses fileName, dateModified, creationTime or DateStamp - I'd like to be able to keep 5 versions of each file and blow away oldest one.
Try this:
gci C:\temp\ -Recurse |
where{-not $_.PsIsContainer} |
Group-Object basename |
foreach {
$_.Group |
sort CreationTime -Descending |
Select -Skip 5 |
foreach { Remove-Item $_.fullname -Force -WhatIf }
}
That will group the files by the basename, then sort/delete within each group.
Remove the -Whatif if the results look right and re-run to actually delete the files.

Powershell Script to get files from last year folder

I have situation where I have 3000 vendors in folder structure. Each vendor then has folders for each year (2001, .... 2014) and other folders as well. Is there a way to list all the files that is in latest year (whichever year).
Basically, I need to upload all the latest agreement files from file-share to SharePoint.
One Liner
Get-ChildItem | %{ $_.FullName | Get-ChildItem [1-9][0-9][0-9][0-9] | sort -Descending | Select-Object -First 1 | Get-ChildItem }
You start from the root folder, for each folder you get all the folders which name looks like a year, sort them, take the first one, and get all it's folders.
Of course, there is a plenty of issues with this. e.g. there has to be at least one year folder, no 'year' files etc. I will leave you tackle that kind of problems.
First I would recursively iterate through all the directories, matching the ones that are equivalent to the current year:
$thisYearDirs = get-childitem -Directory -Recurse | where {$_.Name -eq (get-date).year}
then you would just get the files in each of those:
$thisYearDirs | get-childitem
You could also do it all in one line:
get-childitem -Directory -Recurse | where {$_.Name -eq (get-date).year} | get-childitem
Note that the -directory switch needs powershell v3, you could filter out directories in earlier versions by modifying the where clause condition to do it:
get-childitem -Recurse | where {$_.PSIsCOntainer -and $_.Name -eq (get-date).year} | get-childitem