|
| 1 | +# Get-DirStats.ps1 |
| 2 | +# Written by Bill Stewart (bstewart@iname.com) |
| 3 | +# Outputs file system directory statistics. |
| 4 | + |
| 5 | +#requires -version 2 |
| 6 | + |
| 7 | +<# |
| 8 | +.SYNOPSIS |
| 9 | +Outputs file system directory statistics. |
| 10 | +
|
| 11 | +.DESCRIPTION |
| 12 | +Outputs file system directory statistics (number of files and the sum of all file sizes) for one or more directories. |
| 13 | +
|
| 14 | +.PARAMETER Path |
| 15 | +Specifies a path to one or more file system directories. Wildcards are not permitted. The default path is the current directory (.). |
| 16 | +
|
| 17 | +.PARAMETER LiteralPath |
| 18 | +Specifies a path to one or more file system directories. Unlike Path, the value of LiteralPath is used exactly as it is typed. |
| 19 | +
|
| 20 | +.PARAMETER Only |
| 21 | +Outputs statistics for a directory but not any of its subdirectories. |
| 22 | +
|
| 23 | +.PARAMETER Every |
| 24 | +Outputs statistics for every directory in the specified path instead of only the first level of directories. |
| 25 | +
|
| 26 | +.PARAMETER FormatNumbers |
| 27 | +Formats numbers in the output object to include thousands separators. |
| 28 | +
|
| 29 | +.PARAMETER Total |
| 30 | +Outputs a summary object after all other output that sums all statistics. |
| 31 | +#> |
| 32 | + |
| 33 | +[CmdletBinding(DefaultParameterSetName="Path")] |
| 34 | +param( |
| 35 | + [parameter(Position=0,Mandatory=$false,ParameterSetName="Path",ValueFromPipeline=$true)] |
| 36 | + $Path=(get-location).Path, |
| 37 | + [parameter(Position=0,Mandatory=$true,ParameterSetName="LiteralPath")] |
| 38 | + [String[]] $LiteralPath, |
| 39 | + [Switch] $Only, |
| 40 | + [Switch] $Every, |
| 41 | + [Switch] $FormatNumbers, |
| 42 | + [Switch] $Total |
| 43 | +) |
| 44 | + |
| 45 | +begin { |
| 46 | + $ParamSetName = $PSCmdlet.ParameterSetName |
| 47 | + if ( $ParamSetName -eq "Path" ) { |
| 48 | + $PipelineInput = ( -not $PSBoundParameters.ContainsKey("Path") ) -and ( -not $Path ) |
| 49 | + } |
| 50 | + elseif ( $ParamSetName -eq "LiteralPath" ) { |
| 51 | + $PipelineInput = $false |
| 52 | + } |
| 53 | + |
| 54 | + # Script-level variables used with -Total. |
| 55 | + [UInt64] $script:totalcount = 0 |
| 56 | + [UInt64] $script:totalbytes = 0 |
| 57 | + |
| 58 | + # Returns a [System.IO.DirectoryInfo] object if it exists. |
| 59 | + function Get-Directory { |
| 60 | + param( $item ) |
| 61 | + |
| 62 | + if ( $ParamSetName -eq "Path" ) { |
| 63 | + if ( Test-Path -Path $item -PathType Container ) { |
| 64 | + $item = Get-Item -Path $item -Force |
| 65 | + } |
| 66 | + } |
| 67 | + elseif ( $ParamSetName -eq "LiteralPath" ) { |
| 68 | + if ( Test-Path -LiteralPath $item -PathType Container ) { |
| 69 | + $item = Get-Item -LiteralPath $item -Force |
| 70 | + } |
| 71 | + } |
| 72 | + if ( $item -and ($item -is [System.IO.DirectoryInfo]) ) { |
| 73 | + return $item |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + # Filter that outputs the custom object with formatted numbers. |
| 78 | + function Format-Output { |
| 79 | + process { |
| 80 | + $_ | Select-Object Path, |
| 81 | + @{Name="Files"; Expression={"{0:N0}" -f $_.Files}}, |
| 82 | + @{Name="Size"; Expression={"{0:N0}" -f $_.Size}} |
| 83 | + } |
| 84 | + } |
| 85 | + |
| 86 | + # Outputs directory statistics for the specified directory. With -recurse, |
| 87 | + # the function includes files in all subdirectories of the specified |
| 88 | + # directory. With -format, numbers in the output objects are formatted with |
| 89 | + # the Format-Output filter. |
| 90 | + function Get-DirectoryStats { |
| 91 | + param( $directory, $recurse, $format ) |
| 92 | + |
| 93 | + Write-Progress -Activity "Get-DirStats.ps1" -Status "Reading '$($directory.FullName)'" |
| 94 | + $files = $directory | Get-ChildItem -Force -Recurse:$recurse | Where-Object { -not $_.PSIsContainer } |
| 95 | + if ( $files ) { |
| 96 | + Write-Progress -Activity "Get-DirStats.ps1" -Status "Calculating '$($directory.FullName)'" |
| 97 | + $output = $files | Measure-Object -Sum -Property Length | Select-Object ` |
| 98 | + @{Name="Path"; Expression={$directory.FullName}}, |
| 99 | + @{Name="Files"; Expression={$_.Count; $script:totalcount += $_.Count}}, |
| 100 | + @{Name="Size"; Expression={$_.Sum; $script:totalbytes += $_.Sum}} |
| 101 | + } |
| 102 | + else { |
| 103 | + $output = "" | Select-Object ` |
| 104 | + @{Name="Path"; Expression={$directory.FullName}}, |
| 105 | + @{Name="Files"; Expression={0}}, |
| 106 | + @{Name="Size"; Expression={0}} |
| 107 | + } |
| 108 | + if ( -not $format ) { $output } else { $output | Format-Output } |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +process { |
| 113 | + # Get the item to process, no matter whether the input comes from the |
| 114 | + # pipeline or not. |
| 115 | + if ( $PipelineInput ) { |
| 116 | + $item = $_ |
| 117 | + } |
| 118 | + else { |
| 119 | + if ( $ParamSetName -eq "Path" ) { |
| 120 | + $item = $Path |
| 121 | + } |
| 122 | + elseif ( $ParamSetName -eq "LiteralPath" ) { |
| 123 | + $item = $LiteralPath |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + # Write an error if the item is not a directory in the file system. |
| 128 | + $directory = Get-Directory -item $item |
| 129 | + if ( -not $directory ) { |
| 130 | + Write-Error -Message "Path '$item' is not a directory in the file system." -Category InvalidType |
| 131 | + return |
| 132 | + } |
| 133 | + |
| 134 | + # Get the statistics for the first-level directory. |
| 135 | + Get-DirectoryStats -directory $directory -recurse:$false -format:$FormatNumbers |
| 136 | + # -Only means no further processing past the first-level directory. |
| 137 | + if ( $Only ) { return } |
| 138 | + |
| 139 | + # Get the subdirectories of the first-level directory and get the statistics |
| 140 | + # for each of them. |
| 141 | + $directory | Get-ChildItem -Force -Recurse:$Every | |
| 142 | + Where-Object { $_.PSIsContainer } | ForEach-Object { |
| 143 | + Get-DirectoryStats -directory $_ -recurse:(-not $Every) -format:$FormatNumbers |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +end { |
| 148 | + # If -Total specified, output summary object. |
| 149 | + if ( $Total ) { |
| 150 | + $output = "" | Select-Object ` |
| 151 | + @{Name="Path"; Expression={"<Total>"}}, |
| 152 | + @{Name="Files"; Expression={$script:totalcount}}, |
| 153 | + @{Name="Size"; Expression={$script:totalbytes}} |
| 154 | + if ( -not $FormatNumbers ) { $output } else { $output | Format-Output } |
| 155 | + } |
| 156 | +} |
0 commit comments