Powershell Script to get files from last year folder - powershell

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

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.

Powershell Delete all files apart from the latest file per day

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
}

Removing old folders and keeping the most recent

I have been working on a script of late and have come across a snag. I am in the process of removing folders which are automatically created. I want to delete the older versions of those files whilst keeping the new folders untouched, for example:
18.212.1021.0008 //Created on the 19/11/2018 12:12
18.212.1021.0008_1 //Created on the 19/11/2018 12:23
18.212.1021.0008_2 //Created on the 19/11/2018 12:27
18.212.1021.0008_3 //Created on the 19/11/2018 12:32
I would want to keep 18.212.1021.008_3 so I guess I would need to keep the folder with the most recent creation date.
Please see the code below:
$Versionarray = 13..20
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Recurse | Where-Object {
# Recusivly deletes OneDrive version folders within
# Appdata\local which build up everytime OneDrive
# is installed/script is run
$item = $_
$item -is [System.IO.DirectoryInfo] -and (
$Versionarray | Where-Object { $item.Name.Contains($_) }
)
} | Remove-Item -Recurse -Confirm:$false -ErrorAction SilentlyContinue
If the newest folder you want to keep is also the one with the newest creation time, you can use this simple one-liner:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
If you want to filter out only a specific type of folders by name, you could use a simple regex match. I cannot help you with the exact regex (since I would have to know your folder naming pattern) but it would look something like this:
Get-ChildItem "$env:LOCALAPPDATA\Microsoft\OneDrive" -Directory | where Name -match '\d\d+' | sort CreationTime | select -SkipLast 1 | Remove-Item -Recurse -Force
(Note that this is syntax might not work if you use an old Powershell version. If that's the case, let me know and I will provide a compatible fallback solution.)
UPDATE
In response to your comment: Your requirements are still a bit unclear, but here is something to get you started:
If you want to make sure to only delete folders that "look like" version folders, you can adjust the regex in the where-filter. _\d+$ will match anything with an underscore and numbers at the end:
where $_.Name -match '_\d+$'
If you also want to make sure, that this is actually a versioned copy of another existing folder, you could check that too:
where { $_.FullName -match '^(?<OriginalPath>.+)_\d+$' -and (Test-Path $Matches.OriginalPath) }

Copy the newest file from folder and exclude files by part of the name via PowerShell

I try to copy the newest file from folder (.exe files) by using this type of command:
Get-ChildItem "K:\" -File -include "*.exe" | Where-Object { $_. $_.LastWriteTime like "I don't know which parameter I should type here} Copy-Item -Path $files -Destination "C:\"
I don't want to use complex script with variables and etc. (for now)
Here is folder structure which I have:
Release_OSInstaller_2015_CL287638x64_NoDB.exe
Release_OSInstaller_2015_CL287638x64.exe
Release_OSInstaller_2015_CL287337x64_NoDB.exe
Release_OSInstaller_2015_CL287337x64.exe
And so on. Basically every day a new build is deployed in folder where from I copied the file to my machine (remote).
I need to create a script which will copy the newest build but I want to exclude all files with "NoDB.exe" parameter.
Not sure if it is the fastest way (probably not). But if you are not crawling through huge filesystems
Copy-Item -Path (Get-ChildItem "K:\" -File -include "*.exe" |
Where Name -NotMatch '.*NoDB\.exe$' | Sort-Object -Descending
LastWriteTime | Select-Object -First 1) -Destination "C:\"
should do the trick. (My first answers seems to have been wrong ;) )
And I totally missed the excluding condition so now Martin Brandl Addition is included ^^
In addition to the answer from whatever, you could add a Where condition and skip the NoDBfiles:
Get-ChildItem "K:\" -Filter '*.exe' |
Where Name -NotMatch '.*NoDB\.exe$' |
sort LastWriteTime -Descending |
select -first 1 |
Copy-Item -Destination 'C:\'

How to exclude sub folders

I want to get all "*.exe" files from one folder. It has 3 sub folders. but I want get files from 2 sub folders only. I am trying to use -Exclude in PowerShell. It is working to exclude single file but it is not working to exclude a folder. can anybody tell how to solve it.
This is what I am using the below code it is working to exclude "HWEMNGR.EXE" etc files but Its not allowing me to exclude a sub folder from main folder.
$Files = Get-ChildItem -Path "D:\depot\main" -Recurse -Include "*.exe" -Exclude HWEMNGR.EXE,wmboot.exe,SDClientMobileEdition.exe | % { $_.FullName }
Thanks
-Exclude is good for file names however it does not have a good track record for folder name\path exclusion. You should use a Where-Object clause to address that.
$Files = Get-ChildItem -Path "D:\depot\main" -Recurse -Include "*.exe" -Exclude "HWEMNGR.EXE","wmboot.exe","SDClientMobileEdition.exe" |
Select-Object -ExpandProperty FullName |
Where-Object{$_ -notmatch "\\FolderName\\"}
The snippet % { $_.FullName } was replaced by Select-Object -ExpandProperty FullName which does the same thing. Then we use Where-Object to exclude paths where FolderName is not there. It is regex based so we double up on the slashes. This also helps make sure we exclude folders and not a file that might be called "FolderName.exe"
Alternate approach
Like TheMadTechnician points out you could come at this from another direction and just ensure the files come from the only two folders you really care about. Get-ChildItem will take an array for paths so you could also use something like this.
$paths = "D:\depot\main\install","D:\depot\main\debug"
$excludes = "HWEMNGR.EXE","wmboot.exe","SDClientMobileEdition.exe"
$Files = Get-ChildItem $paths -Filter "*.exe" -Exclude $excludes | Select-Object -ExpandProperty FullName
$paths is the only two folders you want results from. You still exclude the files you dont want and then just return the full file paths.
I will take the following example to explain how I do it:
Get-ChildItem C:\a -include "*.txt" -recurse | %{$_.fullname}
C:\a\b\b1.txt
C:\a\b\b2.txt
C:\a\b\b3.txt
C:\a\c\c1.txt
C:\a\c\c2.txt
C:\a\c\c3.txt
C:\a\d\d1.txt
C:\a\d\d2.txt
Here under C:\a, the subfolders are b,c and d
Now I want *.txt files from b and c to be listed and I want to exclude any file under d subfolder.
To achieve this I introduce a where (also used as "?") condition as follows
Get-ChildItem C:\a -include "*.txt" -recurse | ?{$_.fullname -notlike "C:\a\d\*"} | %{$_.fullname}
C:\a\b\b1.txt
C:\a\b\b2.txt
C:\a\b\b3.txt
C:\a\c\c1.txt
C:\a\c\c2.txt
C:\a\c\c3.txt
I hope this solves your problem.