PowerShell filtering range of file names - powershell

I am trying to write a very simple (as far as I know :-) ) script in PowerShell v2.0.
Every morning I need to look at some files to check if they are up to date.
All of the files are in the same folder.
The files are named like so: from1.rar, from2.rar from13.rar, from14.rar, from27.rar, from29.rar and so on. As you can see, the files are in different ranges. I want to filter the name of the files by a range that I determine. I suppose regex will do the trick, but I don't know how to use it...
What I have for now is just filtering and sorting all of the files by time and name into one table:
Get-ChildItem -filter "*.rar" | sort LastWriteTime -Descending | sort name | Format-Table LastwriteTime, name > C:\Users\user1\Desktop\update.txt
Now I want to break the table to form a number of groups (or smaller tables) from the names of the files.

Something like this should do the trick:
$low = 10
$high = 25
Get-ChildItem -Filter '*.rar' | ? {
$_.Name -match 'from(\d+)\.rar' -and
[int]$matches[1] -gt $low -and
[int]$matches[1] -le $high
} | ...
Demonstration:
PS C:\> $files = 'from1.rar','from13.rar','from14.rar','from27.rar','from29.rar'
PS C:\> $files
from1.rar
from13.rar
from14.rar
from27.rar
from29.rar
PS C:\> $files | ? {
>> $_ -match 'from(\d+)\.rar' -and
>> [int]$matches[1] -gt $low -and
>> [int]$matches[1] -le $high
>> }
>>
from13.rar
from14.rar

Here's one way to use a regex:
$range = 5..10
get-childitem From*.rar |
where {$range -contains ($_.name -replace 'From(\d+)\.rar','$1')}

To break your tables into groups of tables, just add the -GroupBy parameter to the cmdlet Format-Table.
For example, to create a table for each file by its property Name:
Get-ChildItem -filter "*.rar" | sort LastWriteTime -Descending | sort name | Format-Table LastwriteTime, name -GroupBy Name
But that might generate too many groups if you have many files, so you may group the table on the first letter of the Name property, like so:
Get-ChildItem -filter "*.rar" | sort LastWriteTime -Descending | sort name | Format-Table LastwriteTime, name -GroupBy #{name="First Letter";E={ ($_.name).substring(0,1) }}
Or, to group the table on the first two letters of the Name property:
Get-ChildItem -filter "*.rar" | sort LastWriteTime -Descending | sort name | Format-Table LastwriteTime, name -GroupBy #{name="First Letter";E={ ($_.name).substring(0,2) }}

Related

I need my Get-ChildItem string search command print file names along with their Date Modified values, sorted

I spent quite some time searching for the solution of my problem, but found nothing. I have one single folder with mostly .html files, and I frequently need to search to find the files that contain certain strings. I need the search result to be displayed with just the file name (as the file will only be in that one folder) and file's last write time. The list needs to be sorted by the last write time. This code works perfectly for finding the correct files
Get-ChildItem -Filter *.html -Recurse | Select-String -pattern "keyWord string" | group path | select name
The problem with it is that it displays the entire path of the file (which is not needed), it does not show the last write time, and it is not sorted by the last write time.
I also have this code
Get-ChildItem -Attributes !Directory *.html | Sort-Object -Descending -Property LastWriteTime | Select-Object Name, LastWriteTime
That code prints everything exactly as I want to see it, but it prints all the file names from the folder instead of printing only the files that I need to find with a specific string in them.
Since you are only using Select-String to determine if the text exists in any of the files move it inside a Where-Object filter and use the -Quiet parameter so that it returns true or false. Then sort and select the properties you want.
Get-ChildItem -Filter *.html |
Where-Object { $_ | Select-String -Pattern 'keyWord string' -Quiet } |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
For multiple patterns one way you can do it is like this
Get-ChildItem -Filter *.html |
Where-Object {
($_ | Select-String -Pattern 'keyWord string' -Quiet) -and
($_ | Select-String -Pattern 'keyWord string #2' -Quiet)
} |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
And another way using Select-String with multiple patterns which may be a bit faster
$patterns = 'keyword 1', 'keyword 2', 'keyword 3'
Get-ChildItem -Filter *.html |
Where-Object {
($_ | Select-String -Pattern $patterns | Select-Object -Unique Pattern ).Count -eq $patterns.Count
} |
Sort-Object LastWriteTime |
Select-Object Name, LastWriteTime
If you don't care about it being a bit redundant, you can Get-ChildItem the results after your searching:
Get-ChildItem -Filter *.html -Attributes !Directory -Recurse | Select-String -Pattern "keyWord string" | group path | foreach {Get-ChildItem $_.Name } | Sort-Object -Descending LastWriteTime | Select Name,LastWriteTime
After you Select-String you get the attributes of that object instead of the original, so we're taking the results of that object and passing it back into the Get-ChildItem command to retrieve those attributes instead.

Powershell: Get-ChildItem, get latest file of files with similar names

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"
}

How do I group and sort values into a dictionary?

I'm attempting to find the latest version of multiple files within a directory. Currently, I'm calling GCI per file, but that is extremely slow, so I want to instead cache all the results by unique file name and then just perform a lookup in the cache.
I'm currently doing the following:
Gci $filePath -Recurse | ?{ -Not $_.PSIsContainer } | Group-Object Name
I'm trying to convert this to the powershell equivalent of the C# code:
group.ToDictionary(g => g.Key, g => g.Values.OrderByDescending(v => v.ModifiedAt).First().FullName)
How would I accomplish this in Powershell?
What I would do to create a hashtable of files would be to create an empty hashtable, and then populate it with the results of your GCI:
$files = ${}
GCI $filepath -Recurse -File | Group Name | ForEach{ $files.Add($_.Name, ($_.Group | Sort LastWriteTime)) }
Or if all you want is the most recent file, add | Select -Last 1 after the Sort LastWriteTime. If all you care about is the path, you could even do | Select -Last 1 -ExpandProperty FullName.
$Files = ${}
GCI $filepath -recurse | Group Name | ForEach{ $files.Add($_.Name, ($_.Group | Sort LastWriteTime | Select -Last 1 -ExpandProperty FullName)) }

Powershell - count files with same name pattern

I try to count files in a folder which have the same name pattern. In this case every signs before "_" is the important part (the pattern).
Example (c:\temp)
ct24fe_2016-03-01.txt
ct24fe_2016-03-04.txt
ct24fe_2016-03-08.txt
ct24fe_2016-04-01.txt
ct24fe_2016-04-04.txt
xye4ka_2015-03-04.txt
xye4ka_2015-03-08.txt
xye4ka_2015-03-10.txt
xye4ka_2015-03-15.txt
xye4ka_2015-04-01.txt
xye4ka_2015-04-04.txt
zzztgf_2014-04-16.txt
zzztgf_2014-04-18.txt
zzztgf_2014-04-19.txt
zzztgf_2014-05-15.txt
The result should be:
Name | Count
ct24fe | 5
xye4ka | 6
zzztgf | 4
How could I do this?
Thanks for your support.
Group-Object supports scriptblocks for the -Property argument, you can pipe the files directly to it:
Get-ChildItem C:\temp |Group-Object {$_.Name -split '_' |Select -First 1} -NoElement
So I've come up with this:
$input = "your stuff"
$array = New-Object System.Collections.ArrayList
$input | % {$array.Add($_.split("_")[0])}
$array | Group-Object -NoElement
One possibility:
(Get-ChildItem c:\temp -Name) -replace '_.*' | Group-Object -NoElement

How to parse filenames to determine the newest file in each of multiple folders

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.