I am trying to use Get-Content and create a variable that will fill out a directory pathname based on each line of my file.
I have a long list of users in a .csv file and I want to check each of their usernames to get directory size.
The script works if I remove the $username variable and type the username manually at the end of $startdirectory.
$username = Get-Content 'C:\Scripts\AvayaUsers.csv'
$startDirectory = '\\xx\xxxx\xxxxxxxxxxx\users\$username'
$directoryItems = Get-ChildItem $startDirectory | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
foreach ($i in $directoryItems)
{
$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 / 1GB) + " GB"
}
As others have pointed out, you are assigning all users to the end of the path, instead of each user one by one. You need another loop for each user. In addition to that there are a few other things that need attention.
$startDirectory = '\\xx\xxxx\xxxxxxxxxxx\users\$username'
Variables do not expand in single quotes, those should be double quotes.
$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1GB) + " GB"
Seems you couldn't decide between concatenation or interpolation. Some will argue one way and vice versa. I recommend just picking one and staying consistent.
With these issues addressed you could end up with something like this.
$username = Get-Content 'C:\Scripts\AvayaUsers.csv'
foreach ($user in $username)
{
$startDirectory = "\\xx\xxxx\xxxxxxxxxxx\users\$user"
$directoryItems = Get-ChildItem $startDirectory -Directory | Sort-Object
foreach ($i in $directoryItems)
{
$subFolderItems = Get-ChildItem $i.FullName -File -Recurse -Force | Measure-Object -property Length -sum | Select-Object Sum
"{0} -- {1:N2}" -f $i.FullName,"$($subFolderItems.sum / 1GB) GB"
}
}
Also, if it is a CSV it may be preferable to treat it as such. If it's a single list you can still use it as CSV by specifying the header.
$username = Import-Csv -Path \path\to.csv -Header UserName
You're telling the script to basically assign the entire $username as a variable and you don't loop through it.
This means the entire variable with all users is assigned to $startDirectory. I'm quite sure the script will work with only one user in the CSV.
You need to loop with a foreach each user included in the CSV and then get the file size as you sum.
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'm fairly new to Powershell and programming in general. I want to search files using Powershell having multiple conditions. I have managed to write this code
$Drives = Get-PSDrive -PSProvider 'FileSystem'
$Filename= 'Result'
$IncludeExt= '*csv,*docx'
$StartDate= '11/1/20'
$EndDate= '1/26/21'
Get-ChildItem -Path $Drives.Root -Recurse |Where-Object {$IncludeExt -match $_.Extension} | Where-Object { $_.BaseName -match $Filename} | Where-Object {$_.lastwritetime -ge $StartDate -AND $_.lastwritetime -le $EndDate} |
foreach{
$Item = $_.Basename
$Path = $_.FullName
$Type = $_.Extension
$Age = $_.CreationTime
$Path | Select-Object `
#{n="Name";e={$Item}},`
#{n="Created";e={$Age}},`
#{n="filePath";e={$Path}},`
#{n="Folder/File";e={if($Folder){"Folder"}else{$Type}}}`
}| Export-Csv D:\FFNew.csv -NoTypeInformation
This works well when the all variables are mentioned. But how do I get this to work when
Case1: If $Filename is empty then it gives all the files with the mentioned extensions and files modified in Range of dates
Case2: If $IncludeExt is left empty then it gives all files with the $Filename mentioned, currently it gives only the folders and files modified in Range of dates
Case 3: If $Filename and $IncludeExt is left empty it gives all the files modified between the $StartDate and $EndDate
Pranay,
[EDITED]
Ok, here's the revised (exact) script with notes and sample output. Note: you'll have to change the items that are specific to my machine!
$Drives = Get-PSDrive -PSProvider 'FileSystem'
$Filename = "*" #for all or "*partial name*"
$IncludeExt = $Null #for no ext. or "*.csv","*.docx",etc...
$StartDate = '01/1/2020' #to ignore this use 1/1/1920
#For latest date use below otherwise specify date.
$EndDate = (Get-Date).ToShortDateString()
#Note: below uses only 3rd drive in the array remove [2] for all.
$GCIArgs = #{Path = $Drives[2].Root
Recurse = $True
}
If ($Null -ne $IncludeExt) {
$GCIArgs.Add("Include",$IncludeExt)
}
Get-ChildItem #GCIArgs |
Where-Object {($_.BaseName -Like $Filename) -and
($_.lastwritetime -ge $StartDate) -and
($_.lastwritetime -le $EndDate) } |
foreach{
$Item = $_.Basename
$Path = $_.FullName
$Type = $_.Extension
$Type = & {if($_.PSIsContainer){"Folder"}else{$_.Extension}}
$Age = $_.CreationTime
$Path | Select-Object #{n="Name" ;e={$Item}},
#{n="Created" ;e={$Age}} ,
#{n="filePath" ;e={$Path}},
#{n="Folder/File";e={$Type}}
} | Export-Csv -LiteralPath 'G:\BEKDocs\FFNew.csv' -NoTypeInformation
Notes:
$IncludeExt is specified as $Null if it is not used and if used the list is like this ".csv",".docx"
$Filename is specified as "*" for all filenames. Also changed the test from -match to -like so partial filenames should include *, e.g. "partial name".
Notice I changed the location of the check for Extensions to use the -Include parameter of the Get-ChildItem vs checking in the Where-Object.
Changed the piping of data to successive Where-Object clauses and replaced with -and operator, same effect and more efficient.
Changed the test for Directories to use the PSIsContainer property, couldn't see where you were getting the value for $Folder.
Removed the continuation characters from the Select-Object as the comma serves that purpose and is cleaner.
Sample output on Single drive (per code shown above) with some lines hidden for space considerations but notice the last line number.
Sample output on all drives (code edited as per comment in code), again lines hidden for space but showing multiple drives and final line number.
HTH
I am trying to filter folders based off of the count for the number of files in each folder.
I have been able to list the folder name and the value if there is a value greater than 1. I am trying to exclude the folders that may contain no items.
The amount of items changes daily.
$Date2 = Get-Date -Format "yyyy-MM-dd"
$Date2Str = '{0:yyyy-MM-dd}' -f $Date2
$startFolder = "U:\test"
#Returns the Count of files in each queue
$colItems = (Get-ChildItem $startFolder -recurse | Where-Object
{$_.PSIsContainer -eq $True} | Sort-Object)
if($colItems -ine $null){
foreach ($i in $colItems)
{
$subFolderItems = (Get-ChildItem $i.FullName | Where-Object
($_.CreationTime -lt $Date2Str -and $_.Name -like "*.tif"))
$i.Name + " -- " -f ($subFolderItems.Count) |Format-Table
#{Expression={$colItems -ge 1}}
I expect the output of $colItems to be the subFolder name, and the count, excluding any subFolder with a Count less than 1 or equals to 0.
The actual return is the list of subfolder Names and counts with all subFolders including those with a count equal to 0.
If I've got your explanation right you're looking for something like this:
$startFolder = 'U:\test'
Get-ChildItem -Path $startFolder -Directory |
Select-Object -Property Name, #{Name = 'FileCount'; Expression = { (Get-ChildItem -Path $_.FullName -File).count}}
That lists all subfolders of your $startFolder together with their file count.
BTW: This code expects at least Powershell version 3.
... and of course you can pipe this now to a Where-Object and output only the folders with more than one file in it ...
Get-ChildItem -Path $startFolder -Directory |
Select-Object -Property Name, #{Name = 'FileCount'; Expression = { (Get-ChildItem -Path $_.FullName -File).count } } |
Where-Object -Property FileCount -GT -Value 1
I'm having issues with this snippet of code to find the folders in a directory and list with sizes but I keep running into a error
$Directory = Read-Host "Enter your directory"
$colItems = (Get-ChildItem $Directory | {$_.Attributes -match 'Directory'} |Measure-Object -Property Length -Sum )
After adding bit after | to show the sizes I started getting errors.
Directory objects (objects of type DirectoryInfo) don't have a length property, which is why you are getting errors. In order to get the space taken up by directories in powershell you'll have to recursively search through all the subdirectories and adding up the length of all the files that they contain. Fortunately there are several sources available to show you how to do this. Here's one.
You can use the Scripting.FileSystemObject COM object for simplicity.
$Directory = Read-Host "Enter your directory"
$fso = New-Object -ComObject Scripting.FileSystemObject
Get-ChildItem -Path $Directory | ? { $_.PSIsContainer } | % { $fso.GetFolder($_.FullName).size/1kb }
Try this
$Folders = Get-ChildItem $Directory | Where-Object {$_.Attributes -eq 'Directory'}
foreach ($Folder in $Folders)
{
$FolderSize = Get-ChildItem $Folder.Fullname -recurse | Measure-Object -property length -sum
$Folder.FullName + " -- " + "{0:N2}" -f ($FolderSize.sum / 1MB) + " MB"
}
-Recurse Parameter makes sure that apart from getting System.IO.DirectoryInfo you are also getting System.IO.FileInfo objects which contain Length property.
I would also suggest that instead using
$Folders = Get-ChildItem $Directory | Where-Object {$_.Attributes -eq 'Directory'}
You can use:
$Folders = Get-ChildItem $Directory | Where-Object {$_.PSIsContainer -eq $True}
This property will be available for other providers (registry, cert store, etc.) as well.