Listing Folder Size by Piping - powershell

How can I list size of each folder in a directory by the sum of all files in each folder/subfolders?
My latest attempt:
ls | foreach-object { select-object Name, #{Name = "Size"; Expression = { ls $_ -recurse | measure-object -property length -sum } }
I've made other attempts but nothing successful yet. Any suggestions or solutions are very welcome. I feel like I'm missing something very obvious.
The output should look as follows:
Name Size
And it should list each folder in the root folder and the size of the folder counting subfolders of that folder.
I was able to resolve the issue with the following:
param([String]$path)
ls $path | Add-Member -Force -Passthru -Type ScriptProperty -Name Size -Value {
ls $path\$this -recurse | Measure -Sum Length | Select-Object -Expand Sum } |
Select-Object Name, #{Name = "Size(MB)"; Expression = {"{0:N0}" -f ($_.Size / 1Mb)} } | sort "Size(MB)" -descending

I think you've basically got it, honestly.
You could be a bit more elegant by using Add-Member:
ls | Add-Member -Force -Passthru -Type ScriptProperty -Name Length -Value {
ls $this -recurse | Measure -Sum Length | Select -Expand Sum }
PSCX messes with the formatting and will output "" for the size even though you've actually got a size. If you're using PSCX you'll have to add an explicit | Format-Table Mode, LastWriteTime, Length, Name -Auto

It's not particularly elegant but should get the job done:
gci . -force | ?{$_.PSIsContainer} |
%{$res=#{};$res.Name=$_.Name; $res.Size = (gci $_ -r | ?{!$_.PSIsContainer} |
measure Length -sum).Sum; new-object psobject -prop $res}
Note the use of -Force to make sure you're summing up hidden files. Also note the aliases I have used (convenient when typing interactively). There's ? for Where-Object and % for Foreach-Object. Saves the wrists. :-)

Here is a handy Powershell example script that may be adapted to fit what you are looking for.

Related

Add a function to return folder size in Powershell [duplicate]

I have found several resources that use the following script to get folder sizes
$colItems = (Get-ChildItem $startFolder -recurse | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object)
foreach ($i in $colItems)
{
$subFolderItems = (Get-ChildItem $i.FullName | Measure-Object -property length -sum)
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
}
The problem with that is it also lists the subdirectories ie:
c:\test\1 -- 10mb
c:\test\1\folder -- 10mb
c:\test\1\folder\deep -- 5mb
c:\test\1\folder\tuna -- 5mb
c:\test\2 -- 20bm
c:\test\2\folder -- 20mb
c:\test\2\folder\deep -- 10mb
c:\test\2\folder\tuna -- 10mb
I think you know see where I am going. What I am looking for is just the parent folder's results... SO:
c:\test\1 -- 10mb
c:\test\2 -- 20mb
How can this be accomplished with Powershell?
....
You need to get the total contents size of each directory recursively to output. Also, you need to specify that the contents you're grabbing to measure are not directories, or you risk errors (as directories do not have a Length parameter).
Here's your script modified for the output you're looking for:
$colItems = Get-ChildItem $startFolder | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
}
This simple solution worked for me as well.
Get-ChildItem -Recurse 'directory_path' | Measure-Object -Property Length -Sum
The solution posted by #Linga:
"Get-ChildItem -Recurse 'directory_path' | Measure-Object -Property Length -Sum" is nice and short. However, it only computes the size of 'directory_path', without sub-directories.
Here is a simple solution for listing all sub-directory sizes. With a little pretty-printing added.
(Note: use the -File option to avoid errors for empty sub-directories)
foreach ($d in gci -Directory -Force) {
'{0,15:N0}' -f ((gci $d -File -Recurse -Force | measure length -sum).sum) + "`t`t$d"
}
Sorry to reanimate a dead thread, but I have just been dealing with this myself, and after finding all sorts of crazy bloated solutions, I managed to come up with this.
[Long]$actualSize = 0
foreach ($item in (Get-ChildItem $path -recurse | Where {-not $_.PSIsContainer} | ForEach-Object {$_.FullName})) {
$actualSize += (Get-Item $item).length
}
Quickly and in few lines of code gives me a folder size in Bytes, than can easily be converted to any units you want with / 1MB or the like.
Am I missing something? Compared to this overwrought mess it seems rather simple and to the point. Not to mention that code doesn't even work since the called function is not the same name as the defined function. And has been wrong for 6 years. ;)
So, any reasons NOT to use this stripped down approach?
This is similar to https://stackoverflow.com/users/3396598/kohlbrr answer, but I was trying to get the total size of a single folder and found that the script doesn't count the files in the Root of the folder you are searching. This worked for me.
$startFolder = "C:\Users";
$totalSize = 0;
$colItems = Get-ChildItem $startFolder
foreach ($i in $colItems)
{
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
$totalSize = $totalSize + $subFolderItems.sum / 1MB
}
$startFolder + " | " + "{0:N2}" -f ($totalSize) + " MB"
This is something I wind up looking for repeatedly, even though I wrote myself a nice little function a while ago. So, I figured others might benefit from having it and maybe I'll even find it here, myself. hahaha
It's pretty simple to paste into your script and use. Just pass it a folder object.
I think it requires PowerShell 3 just because of the -directory flag on the Get-ChildItem command, but I'm sure it can be easily adapted, if need be.
function Get-TreeSize ($folder = $null)
{
#Function to get recursive folder size
$result = #()
$folderResult = "" | Select-Object FolderPath, FolderName, SizeKB, SizeMB, SizeGB, OverThreshold
$contents = Get-ChildItem $folder.FullName -recurse -force -erroraction SilentlyContinue -Include * | Where-Object {$_.psiscontainer -eq $false} | Measure-Object -Property length -sum | Select-Object sum
$sizeKB = [math]::Round($contents.sum / 1000,3) #.ToString("#.##")
$sizeMB = [math]::Round($contents.sum / 1000000,3) #.ToString("#.##")
$sizeGB = [math]::Round($contents.sum / 1000000000,3) #.ToString("#.###")
$folderResult.FolderPath = $folder.FullName
$folderResult.FolderName = $folder.BaseName
$folderResult.SizeKB = $sizeKB
$folderresult.SizeMB = $sizeMB
$folderresult.SizeGB = $sizeGB
$result += $folderResult
return $result
}
#Use the function like this for a single directory
$topDir = get-item "C:\test"
Get-TreeSize ($topDir)
#Use the function like this for all top level folders within a direcotry
#$topDir = gci -directory "\\server\share\folder"
$topDir = Get-ChildItem -directory "C:\test"
foreach ($folderPath in $topDir) {Get-TreeSize $folderPath}
My proposal:
$dir="C:\temp\"
get-childitem $dir -file -Rec | group Directory | where Name -eq $dir | select Name, #{N='Size';E={(($_.Group.Length | measure -Sum).Sum / 1MB)}}
from sysinternals.com with du.exe or du64.exe -l 1 .
or 2 levels down:
**du -l 2 c:**
Much shorter than Linux though ;)
At the answer from #squicc if you amend this line: $topDir = Get-ChildItem -directory "C:\test" with -force then you will be able to see the hidden directories also. Without this, the size will be different when you run the solution from inside or outside the folder.
I used the great answer of #kohlbrr and improved it a bit. I also turned it into a function you can put in your $PROFILE. This outputs three properties instead of just text: Name, Size (which is a string output of the size converted to MB), and Value which is the raw size value you can use with | sort value
function Get-DirectorySize([string] $rootFolder) {
$colItems = Get-ChildItem $rootFolder | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $colItems) {
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
[PSCustomObject]#{ Name=$i.Fullname; Size="{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"; Value=$subFolderItems.sum }
}
}
The following script provides file sizes from a directory(not going deep) and sizes in bytes.(give your path in $($args[0]) in both the scripts)
Get-ChildItem -Path "$($args[0])" -Recurse -Depth 0 -file| select Length,LastWriteTime,FullName | Export-Csv .\FileAndFolderSizes.csv -NoTypeInformation
The following script provides the folder sizes(depth 0 ) in bytes within the given directory.
$array= #()
Get-ChildItem -Path "$($args[0])" -Recurse -Depth 0 | Where-Object { $_.PSIsContainer } |
ForEach-Object {
$obj = New-Object PSObject
$Size = [Math]::Round((Get-ChildItem -Recurse $_.FullName | Measure-Object Length -Sum -ErrorAction SilentlyContinue).Sum, 2)
$obj |Add-Member -MemberType NoteProperty -Name "FullName" $_.FullName
$obj |Add-Member -MemberType NoteProperty -Name "LastWriteTime" $_.LastWriteTime
$obj |Add-Member -MemberType NoteProperty -Name "Length" $Size
$array +=$obj
}
$array | select Length,LastWriteTime,FullName | Export-Csv .\FileAndFolderSizes.csv -NoTypeInformation -Append
Output is as follows
"Length","LastWriteTime","FullName"
"593408","2/17/2022 10:51:01 PM","C:\a_appli\Partners_Ref\Client64_Rws2\compresslayer.dll"
"286720","2/9/2021 11:52:18 PM","C:\a_appli\Partners_Ref\Client64_Rws2\fdsrtd.dll"
"589312","6/19/2019 10:18:41 PM","C:\a_appli\Partners_Ref\Client64_Rws2\FdswCli.dll"
"13658276","3/18/2022 9:52:16 AM","C:\a_appli\Partners_Ref\Client64_Rws2\PartnersTemplateBuilder"
Interesting how powerful yet how helpless PS can be in the same time, coming from a Nix learning PS. after install crgwin/gitbash, you can do any combination in one commands:
size of current folder:
du -sk .
size of all files and folders under current directory
du -sk *
size of all subfolders (including current folders)
find ./ -type d -exec du -sk {} \;

Powershell - Sorting and selecting files in foreach loop

I'm trying to create a script that will find the most recent build_info files from multiple install locations in a server's directory, select the "version: " text from each file, and compare them to see if they're all the same (which is what we hope for), or if certain install locations have different versions. As a bonus, it would also be nice to have each path's install version have its own variable so that if I have to output any differences, I can say which specific paths have which versions. For example, if something is installed in Path1, Path2, and Path3, I want to be able to say, "all paths are on version 3.5," or "Path1 is version 1.2, Path2 is version 3.5, Path3 is version 4.8."
Here's a neater list of what I'm trying to do:
Loop through folders in a directory.
For each folder, sort the txt files with a specific name in that path by Creation Date descending and select the most recent.
Once it has the most recent files from each path, Select-String a specific phrase from each of them. Specifically, "version: ".
Compare the version from each path and see if all are the same or there are differences, then output the result.
This is what I've been able to write so far:
$Directory = dir D:\Directory\Path* | ?{$_.PSISContainer};
$Version = #();
foreach ($d in $Directory) {
$Version = (Select-String -Path D:\Directory\Path*\build_info_v12.txt -Pattern "Version: " | Select-Object -ExpandProperty Line) -replace "Version: ";
}
if (#($Version | Select -Unique).Count -eq 1) {
Write-Host 'The middle tiers are all on version' ($Version | Select -Unique);
}
else {
Write-Host 'One or more middle tiers has a different version.';
}
I've had to hard code in the most recent build_info files because I'm not sure how to incorporate the sorting aspect into this. I'm also not sure how to effectively assign each path's result to a variable and output them if there are differences. This is what I've been messing around with as far as the sorting aspect, but I don't know how to incorporate it and I'm not even sure if it's the right way to approach this:
$Recent = Get-ChildItem -Path D:\Directory\Path*\build_info*.txt | Sort-Object CreationTime -Descending | Select-Object -Index 0;
You can use Sort-Object and Select-Object to determine the most recent file. Here is a function that you can give a collection of files to and it will return the most recent one:
function Get-MostRecentFile{
param(
$fileList
)
$mostRecent = $fileList | Sort-Object LastWriteTime | Select-Object -Last 1
$mostRecent
}
Here is one possible solution:
Get-ChildItem "D:\Directory\Path" -Include "build_info*.txt" -File -Recurse |
Group-Object -Property DirectoryName |
ForEach-Object {
$_.Group |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1 |
ForEach-Object {
New-Object -TypeName PsCustomObject |
Add-Member -MemberType NoteProperty -Name Directory -Value $_.DirectoryName -PassThru |
Add-Member -MemberType NoteProperty -Name FileName -Value $_.Name -PassThru |
Add-Member -MemberType NoteProperty -Name MaxVersion -Value ((Select-String -Path $_.FullName -Pattern "Version: ").Line.Replace("Version: ","")) -PassThru
}
}
This will produce a collection of objects, one for each directory in the tree, with properties for the directory name, most recent version and the file we found the version number in. You can pipe these to further cmdlets for filtering, etc.

Sum of file folder size based on file/folder name

I have multiple folders across a number of SQL Servers that contain hundreds/thousands of databases. Each database comprises of three elements:
<dbname>.MDF
<dbname>.LDF
<dbname>files (Folder that contains db files/attachments)
I need to marry these files together and add up their total size, does anyone have any advice on how to do this?
EDIT : Just to clarify, I'm currently able to output the filesizes of the MDF/LDF files, I have a separate script that summarises the folder sizes. I need a method of adding together a .MDF/.LDF/DBFiles folder when their name matches. Bearing in mind all of the files are prefixed with the database name.
EDIT #2: The 2 options given so far sum together the .mdf/.ldf files with no problem, but do not add the folder size of the DBFiles folder. Does anyone have any input on how to amend these scripts to include a folder beginning with the same name.
First provided script:
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
Second provided script:
gci "c:\temp" -file -Include "*.mdf", "*.ldf" -Recurse |
group BaseName, DirectoryName |
%{new-object psobject -Property #{FilesAndPath=$_.Name; Size=($_.Group | gci | Measure-Object Length -Sum).Sum } }
EDIT #3:
Thanks to Ansgar (below), the updated solution has done the trick perfectly. Updating question with final solution:
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
Enumerate just the .mdf files from your database folder, then enumerate the files and folders for each basename.
$root = 'C:\db\folder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum
}
}
if you want the sum of sise files database by dir and name file (without extension), try it
gci "c:\temp" -file -Include "*.mdf", "*.ldf" -Recurse |
group BaseName, DirectoryName |
%{new-object psobject -Property #{FilesAndPath=$_.Name; Size=($_.Group | gci | Measure-Object Length -Sum).Sum } }
Modifiy a little the include gci if necessary

Retain values from some pipes for final output

How do you do something like
PS> A | B | C | Format-Table PropertyFromA, PropertyFromB, PropertyFromC
So for example
gci -r -i *.txt | Get-Content | where {$_.Contains("SomeText")} | FormatTable -Property {$_.Directory, $.Name}
In this case gci output will have properties of Directory, Name but these will be lost when I pipe through Get-Content. How do I store this and make use later when piped to Format-Table. Can all this be achieved nicely in a single pipe chain command?
A small modification to your command will work:
gci -r -i *.txt | ? { (gc $_.FullName) -Match "SomeText" } | FormatTable Directory,Name
Arco444 has the right answer for this situation. On the off chance you are not showing us the real reason you are asking this question, or if others make their way here, I am going to show two examples that address this question as well.
Get-ChildItem -Recurse -filter *.txt | ForEach-Object{
$_ | Add-Member -MemberType NoteProperty -Name FileData -Value (Get-Content $_.FullName) -PassThru
} | Where-Object{($_.Filedata).Contains("SomeText")} |
Format-Table name,directory
Get-ChildItem -Recurse -filter *.txt |
Select Name,Directory,#{Label="FileData";Expression={Get-Content $_.FullName}} |
Where-Object{($_.Filedata).Contains("SomeText")} |
Format-Table name,directory
These "oneliners" are both examples that add a property to the objects created by Get-ChildItem. The new property FileData is then what you filter on. This logic can be applied in other ways as well.

create a list of the attributes of files with cmdlet get-member

1)How to create a list of attributes of files with cmdlet get-member and then sort it by last write time?
2)Find total size of files with different extension(for examp total size for all *.html files)
I think the solution for the first task(second task is ok) should be like this(however it doesn't work)
$a=get-childitem . -filter *.html
$n=$a.Length
do{
$isnotsorted=0
for($i=0;$i -lt ($n-1); $i++) {
if ((get-member $a[$i]).LastWriteTime -lt (get-member $a[$i]).LastWRiteTime){
$a[$i],$a[$i+1]=`
$a[$i+1],$a[$i]
$isnotsorted=$i+1
}
}
$n=$isnotsorted
}
until ($n -eq 0)
$a
You don't need to use Get-Member to do this. You can use Sort-Object and Select-Object:
dir C:\ -Force | ? {!$_.PsIsContainer} | Sort LastWriteTime | Select FullName, Attributes
You can use Group-Object and Measure-Object to do this.
((dir D:\Software -Force -Filter *.html | Group Extension).Group | Measure-Object -Sum Length).Sum / 1MB
I'm not sure why you don't want to use Sort-Object -Property LastWriteTime but here is how you would fix your bubble sort code. Remember Get-Member is not the right cmdlet to use to access a properties value.
$a = get-childitem -filter *.html
$n = $a.Length
do {
$isnotsorted = 0
for($i = 0; $i -lt ($n-1); $i++) {
if ( ($a[$i]).LastWriteTime -lt ($a[$i + 1]).LastWRiteTime ) {
$a[$i] , $a[$i+1] = $a[$i+1] , $a[$i]
$isnotsorted = $i + 1
}
}
$n = $isnotsorted
} until ($n -eq 0)
$a
Another thing to note here is that the performance of this algorithm is much worse than just using Sort-Object. My music folder has 1355 files and the above finishes in 83 seconds. Using Sort-Object finishes in 1.7 seconds.
Measure-Command {
get-childitem D:\shares\Music -rec -filter *.m4a | Sort-Object LastWriteTime
}
You don't need Get-Member to display the attributes of files. Just use Get-ChildItem to get the contents of a directory and then pipe them to Sort-Object:
Get-ChildItem -Path $path | Sort-Object -Property 'LastWriteTime'
You can add the -Recurse parameter to Get-ChildItem to list child directories, and add -Force to list files with the Hidden attribute. You can pipe all of this to a Format-* cmdlet if you want to display properties other than those displayed by the standard formatting for files and directories:
Get-ChildItem -Path $path `
| Sort-Object -Property 'LastWriteTime' `
| Format-Table -Property #('Attributes', 'FullName', 'CreationTime')
Get-Member can be used to determine which properties exist on a file or directory object.
You can use Measure-Object with the -Sum switch to add up the Length property of a collection of files:
$htmlFiles = Get-ChildItem -Path $path -Filter '*.html';
$measurement = $htmlFiles | Measure-Object -Property 'Length' -Sum;
$totalHtmlSize = $measurement.Sum;
To generate a table of the total size of each file type you can do something like this:
Get-ChildItem -Path $path `
| Where-Object { $_ -is [IO.FileInfo]; } `
| Group-Object -Property 'Extension' `
| Select-Object `
#{ Name = 'Extension'; Expression = 'Name' }, `
#{ Name = 'TotalSize'; Expression = { `
($_.Group | Measure-Object -Property 'Length' -Sum).Sum } `
} `
| Sort-Object -Property 'Extension';
That retrieves the contents of $path, filters it to only include files, groups the files by the Extension property, projects each group into an object with a property for the extension and a property for the total file size, then sorts the results by extension.