I found a script to give me the size of each subfolder within a directory.
The problem is I don't know where to put the export-csv command to get that output in a CSV.
As a bonus I would also like to throw in a lastmodified property to those subfolders if possible. But I realize that may require a different script entirely.
Here is the script:
$startFolder = "C:\Scripts"
$colItems = (Get-ChildItem $startFolder | Measure-Object -property length -sum)
"$startFolder -- " + "{0:N2}" -f ($colItems.sum / 1MB) + " MB"
$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"
}
I've tried adding | export-csv c:\path to the very end, both inside and outside the bracket. I also tried adding it after the script command, ie c:\script.ps1 | export-csv. Each time I get the error that "An empty pipe element is not allowed."
If you want to process the output of a foreach loop with a pipeline you must either collect its output in a variable:
$results = foreach ($i in $colItems) {
...
}
$results | Export-Csv 'C:\path\to\your.csv' -NoType
or run it in an expression (i.e. in parentheses):
(foreach ($i in $colItems) {
...
}) | Export-Csv 'C:\path\to\your.csv' -NoType
However, personally I'd prefer ForEach-Object over foreach loops (for differences between the two see here), because the former does work with pipelines:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |
ForEach-Object {
...
} |
Export-Csv 'C:\path\to\your.csv' -NoType
With that said, you may not want a loop in the first place. The Export-Csv cmdlet processes a list of objects and writes the properties of the input objects as fields to a CSV file. Since you already have object input you could simply select the properties you want to export:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |̣
Select-Object FullName, LastWriteTime |
Export-Csv 'C:\path\to\your.csv' -NoType
Custom properties can be added for instance as calculated properties:
Get-ChildItem $startFolder -recurse |
Where-Object {$_.PSIsContainer -eq $True} |
Sort-Object |̣
Select-Object FullName, LastWriteTime, #{n='FolderSize';e={
Get-ChildItem $_.FullName |
Measure-Object -Property Length -Sum |
Select-Object -Expand Sum
}} |
Export-Csv 'C:\path\to\your.csv' -NoType
Related
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 {} \;
I have a csv ($HomeDir) file like this:
Users,Comments,HomeDir
user1,account1,c:\folder1
user2,account2,c:\folder2
user3,account3,c:\folder3
I get succesfully LastAccessTime for each subfolder in 'HomeDir' column with this code:
$csv = Import-Csv $HomeDir
foreach ($folder in $csv.HomeDir) {
Get-ChildItem $folder -ErrorAction SilentlyContinue |
? {$_.PSIsContainer -eq "True"} |
Select-Object FullName, #{Name='LastAccessTime'; Expression={$_.LastAccessTime.ToString('yyyyMMdd')}} |
Sort-Object -Descending -Property LastAccessTime |
Export-Csv $newcsv -NoTypeInformation -Append
}
The result of $newcsv is:
"FullName","LastAccessTime"
"c:\folder1\Sub1","20201223"
"c:\folder1\Sub1a","20201223"
"c:\folder1\Sub1b","20201223"
"c:\folder2\Sub2","20201218"
"c:\folder2\Sub2a","20201218"
"c:\folder3\Sub3","20201212"
"c:\folder3\Sub3a","20201215"
"c:\folder3\Sub3b","20181215"
"c:\folder3\Sub3c","20201011"
The questions is: is there a way to assign the related User based on corresponding 'Users' column?
It would also be enough for me to get an output like this
"Users","FullName","LastAccessTime"
"user1","c:\felder1\Sub1","20201223"
"user1","c:\folder1\Sub1a","20201223"
"user1","c:\folder1\Sub1b","20201223"
"user2","c:\folder2\Sub2","20201218"
"user2","c:\folder2\Sub2a","20201218"
"user3","c:\folder3\Sub3","20201212"
"user3","c:\folder3\Sub3a","20201215"
"user3","c:\folder3\Sub3b","20181215"
"user3","c:\folder3\Sub3c","20201011"
Thanks in advance
Instead of pulling the HomeDir property out before the loop, just reference it inside the loop and add the Users property to your Fullname and LastAccessTime properties.
$properties = #{Name='User'; Expression={$user}},
'FullName',
#{Name='LastAccessTime'; Expression={$_.LastAccessTime.ToString('yyyyMMdd')}}
Import-Csv $HomeDir | ForEach-Object {
$user = $_.users
Get-ChildItem $_.HomeDir -ErrorAction SilentlyContinue |
Where-Object {$_.PSIsContainer -eq "True"} |
Select-Object $properties |
Sort-Object -Descending -Property LastAccessTime
} | Export-Csv $newcsv -NoTypeInformation
To make it easier to read I put the properties in a variable beforehand. Also, if you are on powershell version 3 or higher you can use -Directory parameter in lieu of $_.PSIsContainer -eq $true. Finally, if you use the Foreach-Object instead of a foreach loop you can pipe to export and not have to use -Append opening and closing the file several times.
$properties = #{Name='User'; Expression={$user}},
'FullName',
#{Name='LastAccessTime'; Expression={$_.LastAccessTime.ToString('yyyyMMdd')}}
Import-Csv $HomeDir | ForEach-Object {
$user = $_.users
Get-ChildItem $_.HomeDir -Directory -ErrorAction SilentlyContinue |
Select-Object $properties | Sort-Object -Descending -Property LastAccessTime
} | Export-Csv $newcsv -NoTypeInformation
I propose my version.
$HomeDir="c:\temp\input.csv"
$newcsv="c:\temp\output.csv"
Import-Csv $HomeDir | %{
#save user name
$User=$_.Users
Get-ChildItem $_.HomeDir -ErrorAction SilentlyContinue -Directory |
Sort LastAccessTime -Descending |
select #{N='Users'; E={$User}}, FullName, #{N='LastAccessTime'; E={$_.LastAccessTime.ToString('yyyyMMdd')}}
} | Export-Csv $newcsv -NoType
I'm a bit new to PowerShell. I have a working script returning -Line, -Character and -Word to a csv file. I can't figure out how to add the full name of the file into the csv.
get-childitem -recurse -Path C:\Temp\*.* | foreach-object { $name = $_.FullName; get-content $name | Measure-Object -Line -Character -Word} | Export-Csv -Path C:\Temp\FileAttributes.csv
I've tried using Write-Host and Select-Object, but I'm not sure about the syntax.
I've been using the following as a reference.
Results
This is what I'm after
Use Select-Object with a calculated property:
Get-Childitem -recurse -Path C:\Temp\*.* | ForEach-Object {
$fullName = $_.FullName
Get-Content $fullName | Measure-Object -Line -Character -Word |
Select-Object #{ Name = 'FullName'; Expression={ $fullName } }, *
} | Export-Csv -Path C:\Temp\FileAttributes.csv
Note:
Pass -ExcludeProperty Property to Select-Object to omit the empty Property column.
Pass -NoTypeInformation to Export-Csv to suppress the virtually useless first line (the type annotation) in the CSV.
my purpose is to output the files with the extensions that match the ones I store on a .txt file but I don't know how to make it work. The way I am trying to do now does not generate any output or output the files of the extensions that are not on the text file (Extension.txt) I indicate. How am I supposed to fix this?
The content in my .txt file is:
*.xlsx,*.xlsm,*.xlsb,*.xltx,*.xltm,*.xls,*.xml
My current code is as followed:
$fileHeaders = #('country','cDrive','dDrive')
$extensions = ${C:temp:Extension.txt}
$LocContent = Import-Csv "C:\temp\Location.txt" -Header $fileHeaders
$NumberOfDays = Read-Host 'Within how many days the files created would you like to output?'
$SizeOfFile = Read-Host 'Above what size of the files would you like to output (in kb or mb)?'
$Output = ForEach($Row in $LocContent){
if (($Row.country -ne $null) -and ($Row.cDrive -ne $null) -and ($Row.dDrive -ne $null)){
Get-ChildItem $Row.cDrive,$Row.dDrive -Force -Include -Recurse |
$extensions Where-Object LastWriteTime -gt (Get-Date).AddDays(-$NumberOfDays) |
Where-Object {$_.length/$SizeOfFile -gt 1} |
Select-Object -Property #{N='File Basename';E={$_.BaseName}},
#{N='File Extension';E={$_.Extension}},
#{N='size in MB';E={$_.Length/1024kb}},
Directory,
CreationTime,
LastWriteTime,
#{N="Location";E={$Row.country}}
}
$Output | Format-Table -Auto
$Output | Out-Gridview
$Output | Export-Csv '\NewData.csv' -NoTypeInformation
This both generated the files, outputs the files and makes sure they are in your .txt file.
I am not surprised your code wont work. You were doing it fairly poorly. Look forward to seeing you improve.
$extensions = ((Get-Content C:\temp\Extensions.txt) -join ',') -split ',' -replace '\*',''
Foreach($ext in $extensions){
Get-ChildItem "C:\temp" -Recurse | select Name, FullName, CreationTime, Extension | Where-Object {$_.Extension -like $ext} | export-csv C:\Files.csv -NoTypeInformation -append
}
I just cleaned up your code.
Without knowing the content of Location.txt,
if there are repetitions in the drives, wouldn't Output contain dublettes of the files with different country?
Without your environment untested.
## Q:\Test\2018\07\05\SO_51183354.ps1
$fileHeaders = #('country','cDrive','dDrive')
$Extensions = (Get-Content 'C:\temp\Extension.txt') -replace '\*' -split ','
$LocContent = Import-Csv "C:\temp\Location.txt" -Header $fileHeaders |
Where-Object {($_.country -ne $null) -and
($_.cDrive -ne $null) -and
($_.dDrive -ne $null) }
$NumberOfDays = Read-Host 'Max file age in days?'
$SizeOfFile = Read-Host 'Min file size (in kb or mb)?'
$FileAge = (Get-Date).AddDays(-$NumberOfDays)
$Output = ForEach($Row in $LocContent){
Get-ChildItem $Row.cDrive,$Row.dDrive -Force -Recurse |
Where-Object {($_.Extension -in $Extensions) -and
($_.LastWriteTime -gt $FileAge) -and
($_.Length -gt $SizeOfFile) } |
Select-Object -Property `
#{N='File Basename' ;E={$_.BaseName}},
#{N='File Extension';E={$_.Extension}},
#{N='size in MB' ;E={$_.Length/1MB}},
Directory,
CreationTime,
LastWriteTime,
#{N="Location" ;E={$Row.country}}
}
$Output | Format-Table -Auto
$Output | Out-Gridview
$Output | Export-Csv '\NewData.csv' -NoTypeInformation
I've been searching for a script that simply lists folders in a share and their size. I found the following but I'm getting hung up on how to export the output I'm seeing to an easy to read CSV. It amazes me how something so simple has turned into something difficult. Suggestions welcome!
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | 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"
}
Based on your script:
## C:\Users\UserName\Desktop\Test\SO_50359947.ps1
$colItems = Get-ChildItem "$($Env:USERPROFILE)\Desktop" |
Where-Object {$_.PSIsContainer} | Sort-Object
$data = ForEach ($i in $colItems){
$subFolderItems = Get-ChildItem $i.FullName -recurse -force -ea 0|
Where-Object {!$_.PSIsContainer} |
Measure-Object -Property Length -sum | Select-Object Sum
[PSCustomObject]#{
Folder = $i.FullName
Size = "{0,10:N2} MB" -f ($subFolderItems.sum / 1MB)
}
}
$data
#$data | Export-Csv "$($Env:USERPROFILE)\Desktop\your.csv" -NoType
Sample output (on a different tree)
> $data
Folder Size
------ ----
Q:\test\2018\03 0,37 MB
Q:\test\2018\04 0,83 MB
Q:\test\2018\05 383,57 MB
Uncomment the last line to write to a csv file.
Here is one way using custom objects:
Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
This gives output like this:
FolderName SizeMB SubFolders Files
---------- ------ ---------- -----
Folder1 438.38 19 124
Folder2 34925.72 306 3779
To send this to CSV, simply append the following after the last bracket:
| Export-Csv "$home\Desktop\Folders.csv" -NoTypeInformation
There's a couple ways to do this, but I think the least confusing would be to simply add that information to the item using Add-Member. Then output the desired data via Export-Csv.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" |
Where-Object {$_.PSIsContainer -eq $true} |
Sort-Object |
%{ Add-Member -InputObject $_ -NotePropertyName 'FolderSize' -NotePropertyValue (Get-ChildItem $_.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object -ExpandProperty Sum) -PassThru}
$colItems | Select FullName,FolderSize | Export-Csv -NoType
Please assign to some variable after getting all the file details from the desktop/any location and create the excel
$subFolderItems = Get-ChildItem "$home\Desktop" -Directory |
ForEach-Object {
$contents = Get-ChildItem -Path $_.FullName -Recurse
[PsCustomObject]#{
FolderName = $_.Name
SizeMB = [Math]::Round(($contents | Where-Object PsIsContainer -eq $false | Measure-Object -property Length -Sum).Sum / 1MB,2)
SubFolders = ($contents | Where-Object PsIsContainer -eq $true | Measure-Object).Count
Files = ($contents | Where-Object PsIsContainer -eq $false | Measure-Object).Count
}
}
$subFolderItems | out-file C:\Users\thiyagu.a.selvaraj\Desktop\PowerShell\FileSizeOutput.xls
In my opinion, using CSV for your use case isn't the best plan because (in my folder) there are files that are over 1000 MB, so csv will break up some file sizes. To make a tab delimited file, which can just as easily be parsed by most systems, simply modify your scripts output string.
I changed the -- to a tab character, and added an output at the end of the line
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
The final script is below; you will probably need to change output.csv to your prefered output file location.
$colItems = Get-ChildItem "C:\Users\user.name\Desktop" | 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
"{0}`t{1:N2} MB" -f $i.fullname,($subFolderItems.sum / 1MB) >> output.csv
}
If you don't insist on using pure powershell, on Windows 10 (sufficiently new) you can just call bash:
bash -c "du -h | sed 's/\s\+/,/'"
To show how it affects the resulting csv, a longer example:
bash -c "du -m | sed 's/\([0-9]\+\)\s\+\(.*\)/\1MB,\2/'"