-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathGet-DirectorySize.ps1
1285 lines (946 loc) · 60.5 KB
/
Get-DirectorySize.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<#
Get-DirectorySize.ps1
#>
# The -Paths parameter (i.e. $Paths on line 15) with an alias called "-Path" is defined as a string and inserting an extra [] tells PowerShell that multiple values may follow, usually separated by commas.
[CmdletBinding()]
Param (
[Parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
HelpMessage='Which folder, directory or path would you like to target? Please enter a valid file system path to a directory (a full path name of a directory (a.k.a. a folder) i.e. folder path such as C:\Windows). If the path name includes space characters, please add quotation marks around the path name. The -Path parameter accepts a collection of path names (separated by comma) and also takes an array of strings for paths to query.')]
[Alias("Path")]
[string[]]$Paths = "$env:temp",
[string]$ReportPath = "$env:temp",
[string]$Sort = "Size",
[switch]$Descending,
[switch]$Recurse,
[switch]$Audio
)
Begin {
# Establish some common variables. Collect the results to an ArrayList, so that instead the copying the entire array into a new array in each addition (as in normal arrays), just add the latest data to the bottom with .Add(the_data_to_be_added_at_the_bottom) method. .RemoveAt([4]) tries to remove the fifth item and .Insert(0,'added at the beginning') inserts to the first positition and .Insert(7,'added to 8th') could insert something to any arbitrary position.
$computer = $env:COMPUTERNAME
$start_time = Get-Date
$number_of_paths = $Paths.Count
$descending_switch = @{Descending = $Descending}
$recurse_switch = @{Recurse = $Recurse}
$empty_line = ""
$result_list = @()
[System.Collections.ArrayList]$results = $result_list
$titles = @()
$drives = @()
$skipped = @()
$skipped_path_names = @()
[int[]]$steps = @()
[int[]]$loop = @()
$drive_names = @{}
# Reset the counters (important!)
$total_size = 0
$number_of_directories = 0
$x = 0
$num_invalid_paths = 0
# Extra parameters for $Sort which could be used after ValidateSet attribute # Credit: Martin Pugh: "Get-FolderSizes"
Switch ($Sort) {
"size" { $Sort = "raw_size";Break }
"average" { $Sort = "Average File Size (B)";Break }
"written" { $Sort = "Written Ago (h)";Break }
"age" { $Sort = "Age (Days)";Break }
"read" { $Sort = "Read ago (h)";Break }
"created" { $Sort = "Created on";Break }
"changed" { $Sort = "Last Updated";Break }
"updated" { $Sort = "Last Updated";Break }
"last" { $Sort = "Last Updated";Break }
} # switch
# A function to add folder properties
Function Add-FolderObject {
Param (
$directory
)
# Some owner queries may fail, so excluding those errors
$owner = Try {
(Get-Acl $directory.FullName -ErrorAction SilentlyContinue).Owner
} Catch {
# Fill in the Owner with a set value
"Not enough rights to query the Owner of the directory."
} # Catch
# The failure message "Measure-Object : Property "Length" cannot be found in any object(s) input" when querying folders with empty subfolders is suppressed with -ErrorAction SilentlyContinue...
$size_bytes = (Get-ChildItem $directory.FullName -Force -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
$file_count = ( @(Get-ChildItem $directory.FullName @recurse_switch -Force -ErrorAction SilentlyContinue | Where-Object { $_.PSIsContainer -eq $false })).Count
$folder_count = ( @(Get-ChildItem $directory.FullName @recurse_switch -Force -ErrorAction SilentlyContinue | Where-Object { $_.PSIsContainer -eq $true })).Count
$write = (Convert-ElapsedTime((Get-Date) - ($directory.LastWriteTime)))
$read = (Convert-ElapsedTime((Get-Date) - ($directory.LastAccessTime)))
$average_file_size = If ($file_count -gt 0) { [Math]::Round((($size_bytes) / $file_count),0) } Else { $continue = $true }
$volume = New-Object System.IO.DriveInfo (($directory.Root).Name)
$size_percent = [Math]::Round(((($size_bytes) / ($volume.TotalSize)) * 100), 1)
$free_percent_volume = [Math]::Round(((($volume.AvailableFreeSpace) / ($volume.TotalSize)) * 100), 1)
$used_percent_volume = [Math]::Round((((($volume.TotalSize) - ($volume.AvailableFreeSpace)) / ($volume.TotalSize)) * 100), 1)
$volume_total_size = ConvertBytes($volume.TotalSize)
$obj_folder = New-Object PSObject -Property @{
'Age (Days)' = (New-TimeSpan -Start $directory.LastWriteTime).Days
'Attributes' = $directory.Attributes
'Average File Size' = ConvertBytes($average_file_size)
'Average File Size (B)' = $average_file_size
'BaseName' = $directory.BaseName
'Created on' = $directory.CreationTime
'Creation Time' = $directory.CreationTime
'Creation Time (UTC)' = $directory.CreationTimeUtc
'Directory' = $directory.FullName
'Drive (Size)' = [string]$directory.Root + ' (' + $volume_total_size + ')'
'Exists' = $directory.Exists
'Extension' = $directory.Extension
'Files' = $file_count
'Subfolders' = $folder_count
'Folder Name' = $directory.FullName
'Is ReadOnly' = $directory.IsReadOnly
'Last AccessTime' = $directory.LastAccessTime
'Last AccessTime (UTC)' = $directory.LastAccessTimeUtc
'Last Updated' = $directory.LastWriteTime
'Last WriteTime' = $directory.LastWriteTime
'Last WriteTime (UTC)' = $directory.LastWriteTimeUtc
'Name' = $directory.Name
'Owner' = $owner
'Parent' = $directory.Parent
'Read' = [string]$read + ' ago'
'Read ago (h)' = [Math]::Round(((New-TimeSpan -Start $directory.LastAccessTime).TotalHours), 0)
'PS Is Container' = $directory.PSIsContainer
'PSChildName' = $directory.PSChildName
'PSDrive' = $directory.PSDrive
'PSParentPath' = $directory.PSParentPath
'PSPath' = $directory.PSPath
'PSProvider' = $directory.PSProvider
'raw_size' = $size_bytes
'Root' = $directory.Root
'Size' = ConvertBytes($size_bytes)
'Size (%)' = If ($size_bytes -gt 0) { [string]$size_percent + ' %' } Else { $continue = $true }
'VersionInfo' = $directory.VersionInfo
'Written' = [string]$write + ' ago'
'Written Ago (h)' = [Math]::Round(((New-TimeSpan -Start $directory.LastWriteTime).TotalHours), 0)
'Volume Available Free Space (B)' = $volume.AvailableFreeSpace
'Volume Format' = $volume.DriveFormat
'Volume Type' = $volume.DriveType
'Volume Free' = ConvertBytes($volume.AvailableFreeSpace)
'Volume Free (%)' = [string]$free_percent_volume + ' %'
'Volume Is Ready' = $volume.IsReady
'Volume Label' = $volume.VolumeLabel
'Volume Name' = $volume.Name
'Volume Root Directory' = $volume.RootDirectory
'Volume Total Size' = $volume_total_size
'Volume Total Free Space (B)' = $volume.TotalFreeSpace
'Volume Total Size (B)' = $volume.TotalSize
'Volume Used' = ConvertBytes(($volume.TotalSize) - ($volume.AvailableFreeSpace))
'Volume Used (%)' = [string]$used_percent_volume + ' %'
'Volume' = $volume.Name
} # New-Object
Return $obj_folder
} # function (Add-FolderObject)
# Function used to convert bytes to MB or GB or TB # Credit: clayman2: "Disk Space"
function ConvertBytes {
Param (
$size
)
If ($size -eq $null) {
[string]'0 KB'
} ElseIf ($size -eq 0) {
[string]'0 KB'
} ElseIf ($size -lt 1MB) {
$folder_size = $size / 1KB
$folder_size = [Math]::Round($folder_size, 0)
[string]$folder_size + ' KB'
} ElseIf ($size -lt 1GB) {
$folder_size = $size / 1MB
$folder_size = [Math]::Round($folder_size, 1)
[string]$folder_size + ' MB'
} ElseIf ($size -lt 1TB) {
$folder_size = $size / 1GB
$folder_size = [Math]::Round($folder_size, 1)
[string]$folder_size + ' GB'
} Else {
$folder_size = $size / 1TB
$folder_size = [Math]::Round($folder_size, 1)
[string]$folder_size + ' TB'
} # else
} # function (ConvertBytes)
# Function used to convert numerical elapsed time values to text
function Convert-ElapsedTime {
Param (
$elapsed
)
If ($elapsed.Days -ge 2) {
$elapsed_result = [string]$elapsed.Days + ' days ' + $elapsed.Hours + ' h'
} ElseIf ($elapsed.Days -gt 0) {
$elapsed_result = [string]$elapsed.Days + ' day ' + $elapsed.Hours + ' h'
} ElseIf ($elapsed.Hours -gt 0) {
$elapsed_result = [string]$elapsed.Hours + ' h ' + $elapsed.Minutes + ' min'
} ElseIf ($elapsed.Minutes -gt 0) {
$elapsed_result = [string]$elapsed.Minutes + ' min ' + $elapsed.Seconds + ' sec'
} ElseIf ($elapsed.Seconds -gt 0) {
$elapsed_result = [string]$elapsed.Seconds + ' sec'
} Else {
$elapsed_result = [string]''
} # else (if)
If ($elapsed_result.Contains(" 0 h")) {
$elapsed_result = $elapsed_result.Replace(" 0 h"," ")
} If ($elapsed_result.Contains(" 0 min")) {
$elapsed_result = $elapsed_result.Replace(" 0 min"," ")
} If ($elapsed_result.Contains(" 0 sec")) {
$elapsed_result = $elapsed_result.Replace(" 0 sec"," ")
} # if ($elapsed_result: first)
Return $elapsed_result
} # function (Convert-ElapsedTime)
# A function for creating alternating rows in HTML documents # Credit: Martin Pugh: "Get-FolderSizes"
Function Set-AlternatingRows {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[object[]]$lines,
[Parameter(Mandatory=$true)]
[string]$CSS_even_class,
[Parameter(Mandatory=$true)]
[string]$CSS_odd_class
)
Begin {
$class_name = $CSS_even_class
} # Begin
Process {
ForEach ($line in $lines) {
$line = $line.Replace("<tr>","<tr class=""$class_name"">")
If ($class_name -eq $CSS_even_class) {
$class_name = $CSS_odd_class
} Else {
$class_name = $CSS_even_class
} # Else
Return $line
} # ForEach
} # Process
} # function (Set-AlternatingRows)
# Set the progress bar variables
$path_activity = "Retrieving Folder Properties on $computer"
$path_id = 1 # For using more than one progress bar
$folder_activity = " "
$folder_status = "Folders Found: $x"
$folder_id = 2 # For using more than one progress bar
$folder_task = "Currently Processing: Setting the initial search parameters..." # A description of the current operation, which is set at the beginning of each of the steps that increments the progress bar.
# Start the progress bars
Write-Progress -Id $path_id -Activity $path_activity -Status "Step $($steps.Count) of $($number_of_paths * 3)" -CurrentOperation $path -PercentComplete 0
Write-Progress -Id $folder_id -Activity $folder_activity -Status $folder_status -CurrentOperation $folder_task -PercentComplete 0
Write-Verbose "$(Get-Date -Format HH:mm:ss): The script begins..."
} # begin
Process {
ForEach ($path in $Paths) {
# Increment the loop counter
$loop += 1
# In the second loop and onwards
If ($loop.Count -ge 2) {
$y = $number_of_directories
# Reset the lower progress bar
$folder_status = "Folders Found: 0 (Total: $number_of_directories)"
$folder_task = "Currently Processing: Initiating the loop number $($loop.Count)"
Write-Progress -Id $folder_id -Activity $folder_activity -Status $folder_status -CurrentOperation $folder_task -PercentComplete 0
} Else {
$continue = $true
}
<#
__
/_ |
| |
| |
| |
|_|
#> # (Step 1): Test if the path exists
# Increment the step counter and update the upper progress bar
$steps += 1
Write-Progress -Id $path_id -Activity $path_activity -Status "Step $($steps.Count) of $($number_of_paths * 3)" -CurrentOperation $path -PercentComplete -1
If ((Test-Path $path) -eq $false) {
$invalid_path_was_found = $true
# Increment the error counter
$num_invalid_paths++
# Display an error message in console
$empty_line | Out-String
Write-Warning "'$path' doesn't seem to be a valid path name."
$empty_line | Out-String
Write-Verbose "Please consider checking that the path '$path' was typed correctly and that it is a valid file system path, which points to a directory. If the path contains space characters, please add quotation marks around the path name." -verbose
$empty_line | Out-String
Write-Verbose "Which folder, directory or path would you like to target? Please enter a valid file system path to a directory (a full path name of a directory a.k.a. a full path name of a folder (i.e. folder path such as C:\Windows)) after the -Path parameter. The -Path parameter accepts a collection of path names (separated by comma) and also takes an array of strings for paths to query."
$empty_line | Out-String
$skip_text_2 = "Skipping '$path' from the results."
Write-Output $skip_text
# Add the path as an object (with properties) to a collection of skipped paths
$skipped += $obj_skipped = New-Object -TypeName PSCustomObject -Property @{
'Skipped Path Names' = $path
'Owner' = ""
'Created on' = ""
'Last Updated' = ""
'Size' = "-"
'Error' = "The path was not found on $computer."
'raw_size' = 0
} # New-Object
# Add the path name to a list of failed path names
$skipped_path_names += $path
# Add the path name to title
$titles += $path
# Return to top of the program loop (ForEach $path) and skip just this iteration of the loop.
Continue
} Else {
# Resolve path (if path is specified as relative)
$path = Resolve-Path -Path $path
} # else (if)
<#
___
|__ \
) |
/ /
/ /_
|____|
#> # (Step 2): Get the properties of the path (user inputted starting directory root path)
Write-Verbose "$(Get-Date -Format HH:mm:ss): Now working on $path..."
# Increment the step counter and the counter of total number of directories and update the upper progress bar
$steps += 1
$number_of_directories++
Write-Progress -Id $path_id -Activity $path_activity -Status "Step $($steps.Count) of $($number_of_paths * 3)" -CurrentOperation $path -PercentComplete -1
# Update the lower progress bar
$folder_status = "Folders Found: 0 (Total: $number_of_directories)"
$folder_task = "Currently Processing: Working on $path, folder properties enumeration ongoing..."
Write-Progress -Id $folder_id -Activity $folder_activity -Status $folder_status -CurrentOperation $folder_task -PercentComplete 0
# Get the path (user inputted starting directory root path) as an item and define its properties
$root = Get-Item -Path $path -Force
$root_properties = Add-FolderObject $root
# Add the size of the path (starting directory root path) to the total size
$total_size += $root_properties.raw_size
# Add the path properties (starting directory root path) to the final report
$null = $results.Add($root_properties)
# Add the path name to the title row
$titles += $path
# Get the drive information on unique volumes
$drive = New-Object System.IO.DriveInfo (($root.Root).Name)
If ($drive_names.ContainsKey($drive.Name)) {
$continue = $true
} Else {
$drive_names.Add($drive.Name, $drive.TotalSize)
$free_percent = [Math]::Round(((($drive.AvailableFreeSpace) / ($drive.TotalSize)) * 100), 1)
$used_percent = [Math]::Round((((($drive.TotalSize) - ($drive.AvailableFreeSpace)) / ($drive.TotalSize)) * 100), 1)
# Add the drive as an object (with properties) to a collection of drives
$drives += $obj_drive = New-Object -TypeName PSCustomObject -Property @{
'Available Free Space (B)' = $drive.AvailableFreeSpace
'Format' = $drive.DriveFormat
'Type' = $drive.DriveType
'Free' = ConvertBytes($drive.AvailableFreeSpace)
'Free (%)' = [string]$free_percent + ' %'
'Is Ready' = $drive.IsReady
'Label' = $drive.VolumeLabel
'Name' = $drive.Name
'Root Directory' = $drive.RootDirectory
'Total Size' = ConvertBytes($drive.TotalSize)
'Total Free Space (B)' = $drive.TotalFreeSpace
'Total Size (B)' = $drive.TotalSize
'Used' = ConvertBytes(($drive.TotalSize) - ($drive.AvailableFreeSpace))
'Used (%)' = [string]$used_percent + ' %'
'Volume' = $drive.Name
} # New-Object
} # else
<#
____
|___ \
__) |
|__ <
___) |
|____/
#> # (Step 3): Loop through all the subfolders and enumerate the sub-directories according to the recurse-preference parameter
# Increment the step counter and update the upper progress bar
$steps += 1
Write-Progress -Id $path_id -Activity $path_activity -Status "Step $($steps.Count) of $($number_of_paths * 3)" -CurrentOperation $path -PercentComplete -1
$subfolders = Get-ChildItem $path @recurse_switch -Force -ErrorAction SilentlyContinue | Where-Object { $_.PSIsContainer -eq $true }
$subfolder_paths = $subfolders | Select-Object -ExpandProperty FullName
# Remove some error messages (if an empty folder is set as a starting path)
If ($subfolder_paths.Count -eq $null) {
$continue = $true
} Else {
# Find all subdirectories and enumerate the results
ForEach ($folder in $subfolders) {
# Increment the counters and update the lower progress bar
$number_of_directories++
$x++
$folder_status = "Folders Found: $($x - $y + ($loop.Count) - 1) (Total: $number_of_directories)"
$folder_task = "Currently Processing: $folder"
Write-Progress -Id $folder_id -Activity $folder_activity -Status $folder_status -CurrentOperation $folder_task -PercentComplete (( ($x - $y + ($loop.Count) - 2) / $subfolder_paths.Count) * 100)
# Add the subdirectory properties to the final report
$folder_properties = Add-FolderObject $folder
$null = $results.Add($folder_properties)
} # ForEach subfolder
} # else
} # ForEach path
} # Process
End {
# Close the progress bars and try to avoid dividing with a zero when calculating the -PercentComplete
$path_activity = "Retrieving folder attributes on $computer"
$folder_status = "Folders Found: $x"
$folder_task = "Finished retrieving folders."
Write-Progress -Id $path_id -Activity $path_activity -Status "Step $($steps.Count) of $($number_of_paths * 3)" -PercentComplete 100 -Completed
Write-Progress -Id $folder_id -Activity $folder_activity -Status $folder_status -CurrentOperation $folder_task -PercentComplete 100 -Completed
# Display a summary in console
$total_size_in_text = ConvertBytes $total_size
# Display the volume data in console
$empty_line | Out-String
$empty_line | Out-String
$drives.PSObject.TypeNames.Insert(0,"Volumes a.k.a. Drives")
$drives_selection = $drives | Select-Object 'Volume','Label','Format','Type','Is Ready','Free','Free (%)','Total Size','Used','Used (%)' | Sort-Object 'Volume'
$drives_selection | Format-Table -auto
# Catch the Owner-anomalities and notify the user. This final round of enumeration (without another ForEach loop) should probably be done earlier for best results (speed).
ForEach ($result in $results) {
If (($result | Select-Object -ExpandProperty Owner) -like ("*enough rights to query the Owner of*")) {
$invalid_path_was_found = $true
Write-Verbose "Didn't have enough rights to query the Owner of $result"
# Increment the error counter
$num_invalid_paths++
# Add the path as an object (with properties) to a collection of skipped paths
$skipped += $obj_skipped = New-Object -TypeName PSCustomObject -Property @{
'Skipped Path Names' = $result | Select-Object -ExpandProperty Directory
'Owner' = ""
'Created on' = $result | Select-Object -ExpandProperty 'Created on'
'Last Updated' = $result | Select-Object -ExpandProperty 'Last Updated'
'Size' = "-"
'Error' = "Not enough rights to query the Owner of the directory."
'raw_size' = 0
} # New-Object
} Else {
$continue = $true
} # else (if)
} # foreach
If (($invalid_path_was_found) -ne $true) {
$enumeration_went_succesfully = $true
$stats_text = "Total $number_of_directories folders ($total_size_in_text) processed at '$($titles -join ", ")'"
} Else {
$enumeration_went_succesfully = $false
# Display the skipped path names in console
$empty_line | Out-String
$skipped.PSObject.TypeNames.Insert(0,"Skipped Path Names")
$skipped_selection = $skipped | Select-Object 'Skipped Path Names','Size','Error' | Sort-Object 'Skipped Path Names'
$skipped_selection | Format-Table -auto
If ($num_invalid_paths -gt 1) {
$stats_text = "Total $number_of_directories folders ($total_size_in_text) processed. There were $num_invalid_paths skipped path names."
} Else {
$stats_text = "Total $number_of_directories folders ($total_size_in_text) processed. One path name was skipped."
} # else
} # else
$empty_line | Out-String
$empty_line | Out-String
Write-Output $stats_text
$empty_line | Out-String
# Display some results in a pop-up window (Out-GridView) - about 1/2 fits to the pop-up window
$results.PSObject.TypeNames.Insert(0,"Processed Directories")
$results_selection = $results | Select-Object 'Directory','Owner','Size','Size (%)','raw_size','Files','Subfolders','Average File Size','Average File Size (B)','Written','Written Ago (h)','Age (Days)','Read','Read ago (h)','Created on','Last Updated','Drive (Size)','BaseName','Last AccessTime','Last WriteTime','Creation Time','Extension','Is ReadOnly','Exists','PS Is Container','Attributes','Folder Name','Name','Parent','Root','PSParentPath','PSPath','PSProvider','PSChildName','VersionInfo','Volume Available Free Space (B)','Volume Type','Volume Free','Volume Free (%)','Volume Is Ready','Volume Label','Volume Name','Volume Format','Volume Root Directory','Volume Total Size','Volume Total Free Space (B)','Volume Total Size (B)','Volume Used','Volume Used (%)','Volume','Last WriteTime (UTC)','Creation Time (UTC)','Last AccessTime (UTC)','PSDrive'
$results_selection | Sort-Object 'raw_size','Files','Subfolders' -Descending | Out-GridView
# Write all the results to a CSV-file
If ($results_selection -ne $null) {
$results_selection | Export-Csv $ReportPath\directory_size.csv -Delimiter ';' -NoTypeInformation -Encoding UTF8
} Else {
$continue = $true
}
# Find out how long the script took to complete and try to avoid dividing with a zero when displaying speeds and rates
$end_time = Get-Date
$runtime = ($end_time) - ($start_time)
If ($runtime.Days -ge 2) {
$runtime_result = [string]$runtime.Days + ' days ' + $runtime.Hours + ' h ' + $runtime.Minutes + ' min'
} ElseIf ($runtime.Days -gt 0) {
$runtime_result = [string]$runtime.Days + ' day ' + $runtime.Hours + ' h ' + $runtime.Minutes + ' min'
} ElseIf ($runtime.Hours -gt 0) {
$runtime_result = [string]$runtime.Hours + ' h ' + $runtime.Minutes + ' min'
} ElseIf ($runtime.Minutes -gt 0) {
$runtime_result = [string]$runtime.Minutes + ' min ' + $runtime.Seconds + ' sec'
} ElseIf ($runtime.Seconds -gt 0) {
$runtime_result = [string]$runtime.Seconds + ' sec'
} ElseIf ($runtime.Milliseconds -gt 1) {
$runtime_result = [string]$runtime.Milliseconds + ' milliseconds'
} ElseIf ($runtime.Milliseconds -eq 1) {
$runtime_result = [string]$runtime.Milliseconds + ' millisecond'
} ElseIf (($runtime.Milliseconds -gt 0) -and ($runtime.Milliseconds -lt 1)) {
$runtime_result = [string]$runtime.Milliseconds + ' milliseconds'
} Else {
$runtime_result = [string]''
} # else (if)
If ($runtime_result.Contains(" 0 h")) {
$runtime_result = $runtime_result.Replace(" 0 h"," ")
} If ($runtime_result.Contains(" 0 min")) {
$runtime_result = $runtime_result.Replace(" 0 min"," ")
} If ($runtime_result.Contains(" 0 sec")) {
$runtime_result = $runtime_result.Replace(" 0 sec"," ")
} # if ($runtime_result: first)
If (($runtime.TotalMilliseconds) -gt 0) {
$speed = [Math]::Round(($number_of_directories / $runtime.TotalSeconds),1)
$rate = ConvertBytes ($total_size / $runtime.TotalSeconds)
} Else {
$speed = 0
$rate = 0
} # else
# Display the runtime in console
$runtime_text = "The directories were enumerated in $runtime_result (at the rate: $speed folders ($rate) / second)."
Write-Output $runtime_text
$empty_line | Out-String
# [Start Option A]
# (Option A): Create a HTML final report # Credit: Martin Pugh: "Get-FolderSizes"
# Define the HTML header
# In the CSS style section .even and .odd apply to the custom function Set-AlternatingRows (Outlook ignores "nth-child" definitions in CSS).
# So after defining the custom function Set-AlternatingRows the .odd and .even are specified in the CSS style section.
# After ConvertTo-Html has outputted to a pipeline Set-AlternatingRows is then allowed to change lines (from "<tr>" to "<tr class='$class_name'>") in the source code at hand.
# To improve the formatting of HTML code in Visual Studio Code, press Shift + Alt + F and the selected area will be reformatted.
$header = @"
<style>
table {
border-width: 1px;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
th {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
background-color: #6495ED;
}
td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: black;
}
.odd {
background-color: #ffffff;
}
.even {
background-color: #dddddd;
}
</style>
<title>
Directory Size of "$path"
</title>
"@
$total_size = ConvertBytes $total_size
$volumes_summary = $drives_selection | ConvertTo-Html -Fragment -As Table | Set-AlternatingRows -CSS_even_class odd -CSS_odd_class even | Out-String
$pre = "<h1>Directory Size Report</h1><h3>Listing the contents of ""$($titles -join ", ")"" on $computer</h3>"
$post = "<h3>Drive Utilization on $computer</h3> $volumes_summary <h3><p>Total Number of Folders Processed: $number_of_directories<br />Skipped: $($skipped.Count)<br />Total Space Used: $total_size</p></h3>Generated: $(Get-Date -Format g)"
# Bypass the user defined sort options, if folder or file sizes or amounts are involved, so that always report largest files and folders first, and those which have the most content inside
If ($Sort -eq "raw_size") {
$sort_command = { Sort-Object -property @{Expression="raw_size";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Size (%)") {
$sort_command = { Sort-Object -property @{Expression="Size (%)";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Average File Size") {
$sort_command = { Sort-Object -property @{Expression="Average File Size (B)";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Average File Size (B)") {
$sort_command = { Sort-Object -property @{Expression="Average File Size (B)";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Files") {
$sort_command = { Sort-Object -property @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Subfolders") {
$sort_command = { Sort-Object -property @{Expression="Subfolders";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Directory";Descending=$false} }
} Else {
$sort_command = { Sort-Object -property $Sort -Descending:$Descending }
} # Else
# Create the report and save the it to a file
$HTML = $results | Select-Object 'Directory','Owner','Size','Size (%)','raw_size','Files','Subfolders','Average File Size','Average File Size (B)','Written','Written Ago (h)','Age (Days)','Read','Read ago (h)','Created on','Last Updated','Drive (Size)' | Invoke-Command -ScriptBlock $sort_command | ConvertTo-Html -PreContent $pre -PostContent $post -Head $header -As Table | Set-AlternatingRows -CSS_even_class even -CSS_odd_class odd | Out-File $ReportPath\directory_size.html
# Display the report in the default browser
# & $ReportPath\directory_size.html
Start-Process -FilePath "$ReportPath\directory_size.html" | Out-Null
# [End Option A]
# [Start Option B]
# [End Option B]
Write-Verbose "$(Get-Date -Format HH:mm:ss): Script completed."
# Sound the bell if set to do so with the -Audio parameter (ASCII character 7)
If ( -not $Audio ) {
$continue = $true
} Else {
[char]7
} # else
} # End
# [End of Line]
<#
____ _ _
/ __ \ | | (_)
| | | |_ __ | |_ _ ___ _ __ ___
| | | | '_ \| __| |/ _ \| '_ \/ __|
| |__| | |_) | |_| | (_) | | | \__ \
\____/| .__/ \__|_|\___/|_| |_|___/
| |
|_|
/\
/ \
/ /\ \
/ ____ \
/_/ \_\
____
| _ \
| |_) |
| _ <
| |_) |
|____/
# (Option B) Send the Directory Size Report as an HTML-formatted email # Credit: Brian: "Making PowerShell Emails Pretty"
# Define the email settings
$email_server = "email.server.com"
$email_from = "email.address@somewhere.com"
$email_to = "email.address@somewhere.com"
# Convert date to a string and use it in the email subject field
$current_date = Get-Date
$subject_date = $current_date.ToString('yyyy-MM-dd')
$email_subject = "Directory Size of '$path' " + $subject_date
# Define the CSS style for the email message
# @" "@ is a quote block, allowing more "" inside without breaking
# In the CSS style section .even and .odd apply to the custom function Set-AlternatingRows (Outlook ignores "nth-child" definitions in CSS).
# So after defining the custom function Set-AlternatingRows the .odd and .even are specified in the CSS style section.
# After ConvertTo-Html has outputted its strut Set-AlternatingRows is then allowed to change lines (from "<tr>" to "<tr class='$class_name'>") in the source code at hand.
# To improve the formatting of HTML code in Visual Studio Code, press Shift + Alt + F and the selected area will be reformatted.
# http://thesurlyadmin.com/2013/01/21/how-to-create-html-reports/
# Define CSS with a file
$header = Get-Content "\\SERVER1\Share\css.txt"
# Define "CSS" manually
$header = @"
<style>
body {
font-family: "Calibri";
font-size: 9pt;
color: #4C607B;
}
table {
border-width: 1px;
border-style: solid;
border-color: black;
border-collapse: collapse;
}
th,
td {
border: 1px solid #e57300;
border-collapse: collapse;
padding: 5px;
}
th {
font-size: 1.2em;
text-align: left;
background-color: #003366;
color: #ffffff;
}
td {
color: #000000;
}
.even {
background-color: #ffffff;
}
.odd {
background-color: #bfbfbf;
}
</style>
<title>
Directory Sizes for "$path"
</title>
"@
$total_size = ConvertBytes $total_size
$volumes_summary = $drives_selection | ConvertTo-Html -Fragment -As Table | Set-AlternatingRows -CSS_even_class odd -CSS_odd_class even | Out-String
$pre = "<h1>Directory Size Report</h1><h3>Listing the contents of ""$($titles -join ", ")"" on $computer</h3>"
$post = "<h3>Drive Utilization on $computer</h3> $volumes_summary <h3><p>Total Number of Folders Processed: $number_of_directories<br />Skipped: $($skipped.Count)<br />Total Space Used: $total_size</p></h3>Generated: $(Get-Date -Format g)"
# Bypass the user defined sort options, if folder or file sizes or amounts are involved, so that always report largest files and folders first, and those which have the most content inside
If ($Sort -eq "raw_size") {
$sort_command = { Sort-Object -property @{Expression="raw_size";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Size (%)") {
$sort_command = { Sort-Object -property @{Expression="Size (%)";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Average File Size") {
$sort_command = { Sort-Object -property @{Expression="Average File Size (B)";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Average File Size (B)") {
$sort_command = { Sort-Object -property @{Expression="Average File Size (B)";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Files") {
$sort_command = { Sort-Object -property @{Expression="Files";Descending=$true}, @{Expression="Subfolders";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Directory";Descending=$false} }
} ElseIf ($Sort -eq "Subfolders") {
$sort_command = { Sort-Object -property @{Expression="Subfolders";Descending=$true}, @{Expression="Files";Descending=$true}, @{Expression="raw_size";Descending=$true}, @{Expression="Directory";Descending=$false} }
} Else {
$sort_command = { Sort-Object -property $Sort -Descending:$Descending }
} # Else
# Use the Directory Size final Report object as a email message body
$body = $results | Select-Object 'Directory','Owner','Size','Size (%)','raw_size','Files','Subfolders','Average File Size','Average File Size (B)','Written','Written Ago (h)','Age (Days)','Read','Read ago (h)','Created on','Last Updated','Drive (Size)' | Invoke-Command -ScriptBlock $sort_command | ConvertTo-Html -PreContent $pre -PostContent $post -Head $header -As Table | Set-AlternatingRows -CSS_even_class even -CSS_odd_class odd | Out-String
# Send the email
Send-MailMessage -From $email_from -To $email_to -Subject $email_subject -Body $body -BodyAsHtml -SmtpServer $email_server
_____
/ ____|
| (___ ___ _ _ _ __ ___ ___
\___ \ / _ \| | | | '__/ __/ _ \
____) | (_) | |_| | | | (_| __/
|_____/ \___/ \__,_|_| \___\___|
https://community.spiceworks.com/scripts/show/1738-get-foldersizes # Martin Pugh: "Get-FolderSizes"
http://2012sg.poshcode.org/4950 # Joel Reed: "Get-DirectorySize"
http://brianbunke.com/?p=59 # Brian: "Making PowerShell Emails Pretty"
http://powershell.com/cs/media/p/7476.aspx # clayman2: "Disk Space"
http://powershell.com/cs/media/p/24814.aspx # PowerTips Monthly Volume 2: Arrays and Hash Tables
https://technet.microsoft.com/en-us/library/hh849719.aspx # Invoke-Command
https://technet.microsoft.com/en-us/library/hh849912.aspx # Sort-Object
_ _ _
| | | | | |
| |__| | ___| |_ __
| __ |/ _ \ | '_ \
| | | | __/ | |_) |
|_| |_|\___|_| .__/
| |
|_|
#>
<#
.SYNOPSIS
Retrieves the folder sizes of a specified directory or directories.
.DESCRIPTION
Get-DirectorySize returns the size of a directory or directories (paths) specificed
by a parameter called -Path and reports the sizes of the first level of folders (i.e.
the listing is similar to the common "dir" command, but the size of the folders is
shown in the results and the listing of files is omitted).
To query recursively (i.e. including all sub-directories of the sub-directories and
their sub-directories as well and also all other successive sub-directories) a
parameter -Recurse may be added to the query command.
To effectively use Get-DirectorySize, a path, paths or path names to a directory
should be specified (with the -Path parameter), as by default, only $env:temp
gets searched. The paths should be valid file system paths to a directory (a full
path name of a directory (i.e. folder path such as C:\Windows)). In case the path
name includes space characters, quotation marks around the path name are mandatory.
The -Path parameter accepts a collection of path names (separated by comma) and
also takes an array of strings for paths to query.
The directories are queried extensively, a wide array of properties, such as
Directory, Owner, Size, Relative Size (Size (%)), raw_size, File Count, Subfolder
Count, Average File Size, Average File Size (B), Written, Written Ago (h),
Age (Days), Read, Read ago (h), Created on, Last Updated, BaseName, PSChildName,
Last AccessTime, Last WriteTime, Creation Time, Extension, Is ReadOnly, Exists,
PS Is Container, Attributes, VersionInfo, Folder Name, Name, Parent, Root,
PSParentPath, PSPath, PSProvider, Last WriteTime (UTC), Creation Time (UTC),
Last AccessTime (UTC), PSDrive, Volume Available Free Space (B), Volume Type,
Volume Free, Volume Free (%), Volume Is Ready, Volume Label, Volume Name,
Volume Root Directory, Volume Total Size, Volume Total Free Space (B), Volume
Total Size (B), Volume Used, Volume Used (%) and Volume is leveraged from the
directories totaling over 50 headers / columns. The full report is written to
a CSV-file, about 1/2 of the data is displayed in a sortable pop-up window
(Out-GridView) and a Directory Size Report (as a HTML file) with the essential
information is invoked in the default browser.
The -ReportPath parameter defines where the files are saved. The default save
location of the HTML Directory Size Report (directory_size.html) and the adjacent
CSV-file (directory_size.csv) is $env:temp, which points to the current temporary
file location, which is set in the system (- for more information, please see
the Notes section).
While the parameters -Path, -Recurse and -ReportPath (along with the -Audio
parameter(, which emits an audible beep after the query is finished)) modify
holistically the behavior of Get-DirectorySize, the other parameters -Sort
and -Descending toggle how and in which way the data is displayed in the HTML
Directory Size Report. The usage and behavior of each parameter is discussed
in further detail below. This script is based on Martin Pugh's PowerShell
script "Get-FolderSizes"
(https://community.spiceworks.com/scripts/show/1738-Get-DirectorySize).