I am trying to do a restore of my OneNote notebooks but they restore process isn't ideal. It stores copies of the notes and appends a date to them, and a number if there is more than one for each day. I want to grab the latest file (note) and move it to a folder and then I can restore them instead of using their process.
I have tried using gci | select last -1 which only gets me the latest file in the entire directory. But I need each notes latest version. Ideally, I would make a copy, dump it into another directory that matches the current directory name it is in, and get rid of everything after .one, but I would be happy with just a copy of each notes latest version in a directory.
This script can help you.
$dirPath = "<The directory where the files are located>"
Get-ChildItem $dirPath |
Group-Object { ($_.Name -split '\.one' | Select-Object -First 1) } |
Select-Object #{ Name = 'FileName'; Expression = { $_.Name } },
#{ Name = 'LastFileDate'; Expression = { ($_.Group -split ',') |
ForEach-Object { [DateTime](($_ -split ' ')[2] -split '\)')[0] } |
Sort-Object $_.LastFileDate |
Select-Object -Last 1
} } |
ForEach-Object { "{0}.one (On {1:dd-MM-yyyy}).one" -f $_.FileName, $_.LastFileDate }
Using Mathias' suggestion, and expanding on it to get what you wanted (the most recently modified file in each group), here is a solution that should work for you:
$grps = Get-ChildItem | Group-Object {$_.Name -split '\.one' | Select -First 1}
foreach ($group in $grps) {
$group.Group | Sort LastWriteTime | Select -Last 1 | Select Name, FullName, LastWriteTime
}
You can just grab the FullName property of each file there and use that to Copy-Item it somewhere else.
Related
I am trying to pick the right file using file name(timestamp appended in the file name).
I have 3 files: text.041922.061512, text.041922.063016, text.041922.064212. I need pick text.041922.064212 because it was created last which has data and time on the file name itself. How do i achieve this using PowerShell?
Thanks in advance. I would really appreciate it.
My script is this:
Get-ChildItem -Path "c:/demo | Sort-Object { [DateTime]::ParseExact($_.BaseName.Substring(7,13).Replace('.',' '), "MMddyy hhmmss",$null) } | Select-Object -First 1 | Copy-Item -Destination "E:/test/"
Your file names are missing their extension, but assuming the extension doesn't have any numeric digits, you could use -replace '\D+' to remove all non numeric digits from the file names and then the format for ParseExact could be MMddyyHHmmss.
If the files actually don't have an extension, use $_.Name instead of $_.BaseName.
Get-ChildItem -Path "c:/demo" | Sort-Object {
[DateTime]::ParseExact(($_.BaseName -replace '\D+'), 'MMddyyHHmmss', $null)
} -Descending | Select-Object -First 1 | Copy-Item -Destination "E:/test/"
Here is an example that you can use for testing:
[System.IO.FileInfo[]]('text.041922.061512', 'text.041922.063016', 'text.041922.064212') | Sort-Object {
[DateTime]::ParseExact(($_.Name -replace '\D+'), 'MMddyyHHmmss', $null)
} -Descending | Select-Object -Expand Name -First 1
# Returns: text.041922.064212
I got a script to get files from a folder that I then put in a HTML-Table and mail.
In the folder you have files like HR May 2020, HR April 2020, RR May 2021 etc.
Below is the code itself as a sample, this looks for other files but they come every month as well. In total I will filter 8 files.
Get-ChildItem -Path D:\Temp\Test |
Where-Object { $_.Name -match '^RR_Prognos.*|^AllokeringBogNycklar.*' } |
Sort-Object -Property LastWriteTime |
Select-Object LastWriteTime,FullName
Now I am only interested in seeing the latest file of each, so using last or -days, month, hours or similar wont work.
I tried to find a better solution googling it but could not come up with anything that solved the problem.
So I just need to add to the code it it picks the lastest of each file i filter on, the filter is so it does not care about the month in the name.
Edit: Lets say I would use:
Get-ChildItem -Path c:\tm1 | Where-Object { $_.Name -match '^RR_Prognos.*|^AllokeringBogNycklar.*|^Aktivversion.*|^AllokeringNycklar.*|^HR_prognos.*|^KostnaderDK.*|KostnaderProdukt_prognos.*|^Parametrar_prognos.*|ProduktNyckel_prognos apr.*' } | Sort-Object -Property LastWriteTime -Descending | Select-First 8 | Select-Object LastWriteTime,FullName
Then if one file does not come with the batch, it would show the 2nd last one of that as well. Is there a easier way to block that from happening?
Ok, now that you've provided the whole list that you filter against I can write up a real answer. Here we'll group by file name, then sort each group and grab the last one from each group:
Get-ChildItem -Path c:\tm1 |
Where-Object { $_.Name -match '^RR_Prognos.*|^AllokeringBogNycklar.*|^Aktivversion.*|^AllokeringNycklar.*|^HR_prognos.*|^KostnaderDK.*|KostnaderProdukt_prognos.*|^Parametrar_prognos.*|ProduktNyckel_prognos apr.*' } |
Group {$_.Name -replace '.*?(^RR_Prognos|^AllokeringBogNycklar|^Aktivversion|^AllokeringNycklar|^HR_prognos|^KostnaderDK|KostnaderProdukt_prognos|^Parametrar_prognos|ProduktNyckel_prognos apr).*','$1'} |
ForEach-Object {
$_.Group |
Sort-Object -Property LastWriteTime -Descending |
Select -First 1
} |
Select-Object LastWriteTime,FullName
This might be what you are looking for. This iterates over each search string where you need the newest file.
$searchStrings = #('^pattern1*', '^pattern2*', '^pattern3*')
foreach($searchString in $searchStrings) {
$items = Get-ChildItem -Path $folder | Where-Object { $_.Name -match "$searchString" } | Sort-Object -Property LastWriteTime -Descending
$newestItem = $items[0]
Write-Host "newest item for '$searchString' is $newestItem"
}
I have a file directory which contains approx. 600 employee image files which have been copied from an alternative source.
The filename format is:
xxxxxx_123456_123_20141212.jpg
When the employee image file is updated it just creates another file in the same location and only the datetime changes at the end.
I need to be able to identify the most recent file, however i need to establish first of all which files are 'duplicated'.
My initial thoughts were to try and match the first 14 characters and, if they matched, work out the recent modified date and then delete the older file.
This requires PowerShell version 3.
$Path = 'C:\Users\madtomvane\Documents\PowerShellTest'
#Get the files #Group them by name #Select the most resent file
$FilesToKeep = Get-ChildItem $Path -Recurse -File | Group-Object -Property {$_.Name[0..14]} | ForEach-Object {$_.Group | Sort-Object -Property LastWriteTime -Descending | Select-Object -First 1}
#Get the files #Group them by name #Where there is more than one file in the group #Select the old ones
$FilesToRemove = Get-ChildItem $Path -Recurse -File | Group-Object -Property {$_.Name[0..14]} | Where-Object {$_.Group.Count -gt 1} | ForEach-Object {$_.Group | Sort-Object -Property LastWriteTime -Descending | Select-Object -Skip 1}
$FilesToRemove | Remove-Item
I have logs that are getting written from various Linux servers to a central windows NAS server. They're in E:\log in the format:
E:\log\process1\log20140901.txt,
E:\log\process2\20140901.txt,
E:\log\process3\log-process-20140901.txt,
etc.
Multiple files get copied on a weekly basis at the same time, so created date isn't a good way to determine what the newest file is. Therefore I wrote a powershell function to parse the date out, and I'm attempting to iterate through and get the newest file in each folder, using the output of my function as the "date". I'm definitely doing something wrong.
Here's the Powershell I've written so far:
Function ReturnDate ($file)
{
$f = $file
$f = [RegEx]::Matches($f,"(\d{8})") | Select-Object -ExpandProperty Value
$sqlDate = $f.Substring(0,4) + "-" + $f.substring(4,2) + "-" + $f.substring(6,2)
return $sqlDate
}
Get-ChildItem E:\log\* |
Where {$_.PsIsContainer} |
foreach-object { Get-ChildItem $_ -Recurse |
Where {!$_.PsIsContainer} |
ForEach-Object { ReturnDate $_}|
Sort-Object ReturnDate -Descending |
Select-Object -First 1 | Select Name,ReturnDate
}
I seem to be confounding properties and causing "You cannot call a method on null-valued expression errors", but I'm uncertain what to do from here.
I suspect your $f variable is null and you're trying to invoke a method (Substring) on a null value. Try this instead:
Get-ChildItem E:\Log -File -Recurse | Where Name -Match '(\d{8})\.' |
Foreach {Add-Member -Inp $_ NoteProperty ReturnDate ($matches[1]) -PassThru} |
Group DirectoryName |
Foreach {$_.Group | Sort ReturnDate -Desc | Select -First 1}
This does require V3 or higher. If you're on V1 or V2 change it to this:
Get-ChildItem E:\Log -Recurse |
Where {!$_.PSIsContainer -and $_.Name -Match '(\d{8})\.'} |
Foreach {Add-Member -Inp $_ NoteProperty ReturnDate ($matches[1]) -PassThru} |
Group DirectoryName |
Foreach {$_.Group | Sort ReturnDate -Desc | Select -First 1}
Your code was ok for me when i tried it up until you did a select you were requesting name and returndate when those properties did not exist. Creating a custom object with those values would make your code work. Also i removed some of the logic from your pipes. End result should still work though (I just made some dummy files to test with like your examples).
Working with your original code you could have something like this. This would only work on v3 or higher. Simple changes could make it work on lower if need be. Mostly where [pscustomobject] is concerned.
Function ReturnDate ($file)
{
$f = $file
$f = [RegEx]::Matches($f,"(\d{8})") | Select-Object -ExpandProperty Value
$sqlDate = $f.Substring(0,4) + "-" + $f.substring(4,2) + "-" + $f.substring(6,2)
[pscustomobject] #{
'Name' = $file.FullName
'ReturnDate' = $sqlDate
}
}
Get-ChildItem C:\temp\E\* -Recurse |
Where-Object {!$_PSIsContainer} |
ForEach-Object{ReturnDate $_} |
Sort-Object ReturnDate -Descending |
Select-Object -First 1
The Sort-Object cmdlet supports sorting by a custom script block and will sort by whatever the script block returns. So, use a regular expression to grab the timestamp and return it.
Get-ChildItem E:\log\* -Directory |
ForEach-Object {
Get-ChildItem $_ -Recurse -File |
Sort-Object -Property {
if( $_.Name -match '(\d{8})' )
{
return $Matches[1]
}
Write-Error ('File ''{0}'' doesn't contain a timestamp in its name.' -f $_.FullName)
} |
Select-Object -Last 1 |
Select Name,ReturnDate
}
Note that Select-Object -First 1 was changed to Select-Object -Last 1, since dates would be sorted from oldest to newest.
I have a directory which contains .xls files named as follows:
Recon_[Account No]_[YYYYMMDD].xls
e.g:
Recon_9020111006076954416_20131216.xls
The Account number can be any number of characters between 16 and 20.
I need to get a list of items in this folder - but only the account numbers.
What would be FANTASTIC is if I could get a list of account numbers and then next to them the date the file was created (which can be the datestamp in the file name or the last modified date of the file), in YYYY/MM/DD format, sorted by date.
Like this:
9020111006076954416 2013/12/16
10201129080000235952 2013/12/17
I then need this list of accounts in a text file, or even better, an excel file. Any ideas?
Give this a try:
Get-ChildItem -Filter *.xls | Where-Object {$_.BaseName -match '^Recon_\d{16,20}_\d+$'} | ForEach-Object{
$id,$date = $_.BaseName.Split('_')[1..2]
New-Object PSObject -Property #{
AccountNumber = $id
DateCreated = $date.Insert(4,'/').Insert(7,'/')
}
} | Export-Csv itemList.csv -NoTypeInformation
Fairly, easy, actually:
First obtain the raw data
Get-ChildItem *.xls |
Then extract the properties you need from it:
ForEach-Object {
if ($_.Basename -match '^Recon_(?<account>\d+)_(?<date>\d+)$') {
$_ | Add-Member NoteProperty AccountNumber $Matches.account
}
} |
Select those properties you are interested in (we are still dealing with the original FileInfo object, we just added the account number to it):
Select-Object AccountNumber,LastWriteTime
You could make the header nicer as well:
Select-Object #{l='Account Number'; e={$_.AccountNumber}}, #{l='Date'; e={$_.LastWriteTime}}
At this point you have something you can display on screen nicely. You can then continue formatting the data by piping it into another ForEach-Object:
ForEach-Object {
'{0}`t{1:yyyy-MM-dd}' -f $_.AccountNumber, $_.LastWriteTime
}
or convert it to CSV (which Excel can then open) by piping into ConvertTo-Csv:
ConvertTo-Csv foo.csv
To recap:
$data = Get-ChildItem *.xls |
ForEach-Object {
if ($_.Basename -match '^Recon_(?<account>\d+)_(?<date>\d+)$') {
$_ | Add-Member NoteProperty AccountNumber $Matches.account
}
} |
Select-Object AccountNumber,LastWriteTime
$data | ForEach-Object {
"{0}`t{1:yyyy-MM-dd}" -f $_.AccountNumber, $_.LastWriteTime
} | Out-File foo.txt
$data | ConvertTo-Csv foo.csv
Get all files of this directory. in a loop get file name and split it by '_'. the second item of array is account number and the third one is date.