Count Files by Name - powershell

I am looking for a way to count files from many sub-folders but the tricky part is that i want to filter them by part of their names. To be more specific, all files have a date at the middle of their names. If I want to just count the files within a specific folder I use this:
dir * |%{$_.Name.SubString(7,8)} | group |select name,count|ft -auto
And works like a charm. The problem lies that it cannot see more than one folder. Also a second problem is that in the result, I want to see the path name of the grouped counts. I am also testing this:
dir -recurse | ?{ $_.PSIsContainer } | %{ Write-Host $_.FullName (dir $_.FullName | Measure-Object).Count }
but I cannot implement the date filter from inside the name in this functions. I am also attaching an example of how is the format and how I would like the results.
Any help?

I am looking for a way to count files from many sub-folders but the tricky part is that I want to filter them by part of their names. To be more specific, all files have a date at the middle of their names.
It is not 100% clear to me, if you really want to filter them or to group them before counting, so I'll show both.
Assuming that this middle of their names is, e.g., delimited by _ this can be achieved the following way:
# C:/temp/testFolder/myName_123_folder/text.txt
Get-ChildItem * -Recurse |
Select-Object -Property Name, #{Name = "CustomDate"; Expression = {$_.Name.Split("_")[1]}} |
#This is how you would _filter_
#Where-Object {$_.Custom -eq "123"} |
Group-Object -Property CustomDate |
Select-Object Name, Count
Don't forget to check if the file name matches this pattern, before splitting. This can be done with a Select-Object statement between gci and 1. select, which checks the file name for your specific pattern.
Your question shows also that you wanted to filter for only directories:
dir -recurse | ?{ $_.PSIsContainer } | %{ #[...]
Which is not very efficient.
From the Docs of Get-ChildItem:
-Directory
Gets directories (folders).
To get only directories, use the -Directory parameter and omit the -File parameter. To exclude directories, use the -File parameter and omit the -Directory parameter, or use the -Attributes parameter.
This means, the preferred way to search only for Directories is:
Get-ChildItem -Recurse -Directory | % { #[...]

Related

Powershell Folder List Filter

I have a folder and inside has a list of subfolders/files
Folders
2022
20221101
20221103
20221107
20221108
test123
results
test.txt
Using Powershell
How do get the list of folders that are dates.
How do I get the second latest folder (20221107).
This is what I was able to come with so far:
Get-ChildItem "C:\code\Test" -Filter "2022*" | Sort-Object Name -Descending
You can use TryParseExact method from DateTime to parse the folders names and avoid any type of error, this can be combined with Sort-Object and Select-Object to get the 2nd latest. I have added -Directory to output only folders. Also changed your filter to -Filter "2022????" to ensure you're matching folders that start with 2022 and are followed by 4 characters.
$ref = [ref] [datetime]::new(0)
Get-ChildItem "C:\code\Test" -Filter "2022????" -Directory | Sort-Object {
$result = [datetime]::TryParseExact(
$_.Name,
'yyyyMMdd',
[cultureinfo]::InvariantCulture,
[Globalization.DateTimeStyles]::AssumeLocal,
$ref
)
if($result) { $ref.Value }
} -Descending | Select-Object -Index 1
If you want to ensure you're matching folders with a date format (because -Filter may be too permissive), you can pipe to Where-Object for additional filtering via regex:
$ref = [ref] [datetime]::new(0)
Get-ChildItem "C:\code\Test" -Filter "2022????" -Directory |
Where-Object Name -Match '^2022\d{4}$' | Sort-Object {
# rest of the code here
} -Descending | Select-Object -Index 1
To provide an alternative to Santiago's helpful answer:
Since the timestamps that your folder names represent sort lexically in a way that is equivalent to their chronological sorting, you may not need to convert them to [datetime] instances, and can sort them as-is.
Get-Item C:\code\Test\* -Include 2022???? |
Sort-Object Name -Descending |
Select-Object -Index 1
Note the use of -Include instead of -Filter (which in turn necessitates ending the -Path argument with \* and using Get-Item instead of Get-ChildItem), because the -Filter parameter has legacy quirks that prevent character-exact matching with multiple ? wildcards - see this answer for background information.
Unfortunately, as this solution and the linked answer shows, making -Include (and -Exclude) work as intended is tricky as of PowerShell 7.2.x, and requires memorizing non-intuitive rules.
On the plus side, -Include, which (unlike -Filter) uses PowerShell's wildcard expressions, would also allow you to create a more specific pattern, such as -Include 2020[0-1][0-9][0-3][0-9] (which still isn't strict enough to rule out invalid digit combinations, however).

How can I use PowerShell or a cmd "dir" to get the contents of multiple, but similar paths?

For example, I want the contents of the "Last" folder in the structure below. The various path structures are identical except for the first two levels.
C:\zyx-wvu\abc\Level3\Last
C:\tsr-qpo\def\Level3\Last
C:\nml-kji\ghi\Level3\Last
In PowerShell I get close with:
Get-ChildItem -Path C:\*-*\*
...but it doesn't return any results (as in it never finishes) when I try:
Get-ChildItem -Path C:\*-*\*\Level3
Get-ChildItem -Path C:\*-*\*
will only show you what's in the second layer behind anything with a hyphen in c:\
aka it will show
c:\1-2\alpha
c:\1-5\beta
etc...
What you want is
Get-ChildItem -Path C:\*-*\*\*
or more likely you want
Get-ChildItem -Path C:\*-*\* -recurse
if you want to find paths with the SAME name... you could group them together, and pull out anything with more than one finding... you didn't ask very specifically what you wanted, but here's some ideas.
get-childitem -Path c:\*-*\*\* | group-object -property basename | where count -gt 1 | select -expand group

PowerShell find most recent file

I'm new to powershell and scripting in general. Doing lots of reading and testing and this is my first post.
Here is what I am trying to do. I have a folder that contains sub-folders for each report that runs daily. A new sub-folder is created each day.
The file names in the sub-folders are the same with only the date changing.
I want to get a specific file from yesterday's folder.
Here is what I have so far:
Get-ChildItem -filter “MBVOutputQueriesReport_C12_Custom.html” -recurse -path D:\BHM\Receive\ | where(get-date).AddDays(-1)
Both parts (before and after pipe) work. But when I combine them it fails.
What am I doing wrong?
What am I doing wrong?
0,1,2,3,4,5 | Where { $_ -gt 3 }
this will compare the incoming number from the pipeline ($_) with 3 and allow things that are greater than 3 to get past it - whenever the $_ -gt 3 test evaluates to $True.
0,1,2,3,4,5 | where { $_ }
this has nothing to compare against - in this case, it casts the value to boolean - 'truthy' or 'falsey' and will allow everything 'truthy' to get through. 0 is dropped, the rest are allowed.
Get-ChildItem | where Name -eq 'test.txt'
without the {} is a syntax where it expects Name is a property of the thing coming through the pipeline (in this case file names) and compares those against 'test.txt' and only allows file objects with that name to go through.
Get-ChildItem | where Length
In this case, the property it's looking for is Length (the file size) and there is no comparison given, so it's back to doing the "casting to true/false" thing from earlier. This will only show files with some content (non-0 length), and will drop 0 size files, for example.
ok, that brings me to your code:
Get-ChildItem | where(get-date).AddDays(-1)
With no {} and only one thing given to Where, it's expecting the parameter to be a property name, and is casting the value of that property to true/false to decide what to do. This is saying "filter where *the things in the pipeline have a property named ("09/08/2016 14:12:06" (yesterday's date with current time)) and the value of that property is 'truthy'". No files have a property called (yesterday's date), so that question reads $null for every file, and Where drops everything from the pipeline.
You can do as Jimbo answers, and filter comparing the file's write time against yesterday's date. But if you know the files and folders are named in date order, you can save -recursing through the entire folder tree and looking at everything, because you know what yesterday's file will be called.
Although you didn't say, you could do approaches either like
$yesterday = (Get-Date).AddDays(-1).ToString('MM-dd-yyyy')
Get-ChildItem "d:\receive\bhm\$yesterday\MBVOutputQueriesReport_C12_Custom.html"
# (or whatever date pattern gets you directly to that file)
or
Get-ChildItem | sort -Property CreationTime -Descending | Select -Skip 1 -First 1
to get the 'last but one' thing, ordered by reverse created date.
Read output from get-date | Get-Member -MemberType Property and then apply Where-Object docs:
Get-ChildItem -filter “MBVOutputQueriesReport_C12_Custom.html” -recurse -path D:\BHM\Receive\ | `
Where-Object {$_.LastWriteTime.Date -eq (get-date).AddDays(-1).Date}
Try:
where {$_.lastwritetime.Day -eq ((get-date).AddDays(-1)).Day}
You could pipe the results to the Sort command, and pipe that to Select to just get the first result.
Get-ChildItem -filter “MBVOutputQueriesReport_C12_Custom.html” -recurse -path D:\BHM\Receive\ | Sort LastWriteTime -Descending | Select -First 1
Can do something like this.
$time = (get-date).AddDays(-1).Day
Get-ChildItem -Filter "MBVOutputQueriesReport_C12_Custom.html" -Recurse -Path D:\BHM\Receive\ | Where-Object { $_.LastWriteTime.Day -eq $time }

Powershell - Find files that match a pattern for specific number of times

To find a simple pattern in a set of files in powershell I go
$pattern= 'mypattern'
$r= Get-ChildItem -Path "C:\.." -recurse |
Select-String -pattern $pattern | group path | select name
$r | Out-GridView
In my scenario, I have files that contain the pattern for more than one time and others that have the pattern for one time only. So I am interested in those files that contain the pattern for more than one time and not interested in the rest. Thanks
One approach for the start of what you are looking for is Select-String and Group-Object like you already have.
Select-String -Path (Get-ChildItem C:\temp\ -Filter *.txt -Recurse) -Pattern "140" -AllMatches |
Group-Object Path |
Where-Object{$_.Count -gt 1} |
Select Name, Count |
Out-GridView
This will take all the txt files in the temp directory and group them by the number of matches. -AllMatches is important as by default Select-String will only return the first match it finds on a line.
Of those groups we take the ones where the count is higher than one using Where-Object. Then we just output the file names and there counts with a Select Name,Count. Where name is the full file path where the matched text is located.
About Out-GridView
I see that you are assinging the output from Out-GridView to $r. If you want to do that you need to be sure you add the -PassThru parameter.

Run a function on each element of a list in powershell

I have a directory full of file pairs. Each pair of files have the same name with the extensions of mp3 and cdg (karaoke files!). I would like to use powershell to get the list of all distinct file names with no extensions
I've gotten as far as:
dir -recurse -filter "*.mp3" | select-object Name | sort
But I can't quite figure out how to pass each Name to [System.IO.Path]::GetFileNameWithoutExtension
how would I do this?
What you're looking for is the for-each (%) filter (not precisely sure if it's a filter or a cmdlet but it has the same usage syntax).
Try the following
dir -recurse -filter "*.mp3" |
%{ $_.Name } |
%{ [IO::Path]::GetFileNameWithoutExtension($_) } |
sort
EDIT Update
I changed my answer from "select-object Name" to "%{ $_.Name}". The former essentially takes the Name property off of the pipeline value and creates a new object with a single property of the specified name with the value on the original object. The latter will process every value in the pipeline and pass the result of executing $_.Name down the pipeline.
dir -recurse -filter "*.mp3"| select #{name='Name';Expression={[System.IO.Path]::GetFileNameWithoutExtension($_.Name)}} | sort
If you hate typing %{$_.foo} all the time like I do, try Get-PropertyValue (alias: gpv) from PSCX.
More musings here for the suitably geeky: http://richardberg.net/blog/?p=55
Now that PowerShell v2 is RTMd, you can select the BaseName member:
dir -recurse -filter *.mp3 | select BaseName | sort