Reputation: 658
I have a section of a PowerShell script that gets the file size of a specified directory.
I am able to get the values for different units of measurement into variables, but I don't know a good way to display the appropriate one.
$DirSize = "{0:N2}" -f (($DirArray | Measure-Object -property length -sum).sum)
$DirSizeKB = "{0:N2}" -f (($DirArray | Measure-Object -property length -sum).sum / 1KB)
$DirSizeMB = "{0:N2}" -f (($DirArray | Measure-Object -property length -sum).sum / 1MB)
$DirSizeGB = "{0:N2}" -f (($DirArray | Measure-Object -property length -sum).sum / 1GB)
If the number of bytes is at least 1 KB I want the KB value displayed. If the number of KBs is at least 1 MB I want MBs displayed and so on.
Is there a good way to accomplish this?
Upvotes: 28
Views: 141459
Reputation: 10174
If you have WSL then you can invoke it directly from PWSH:
wsl ls -lh
even better if you use exa:
wsl exa -l /mnt/c
one drawback is the tab completion will give you .\folder1\folder2
which does not work with exa
, so I write a custom function replacing the slash:
Function ll {
$target = '.' #default to current directory is no command-line arguments supplied
if($args[0]){
$target = $args[0] -replace "`\\","`/"
}
wsl exa -l --group --icons --sort=modified $target
}
!!! Put above in your $PROFILE
file by notepad.exe $PROFILE
, and reload pwsh with . $PROFILE
.
Now ll
will show info nicely and tab completion will work with sub directories like
ll .\subDir\subSubDir.
Upvotes: 0
Reputation: 41
I've added the function DisplayInBytes($num) in the Bill Stewart "d.ps1" script
function DisplayInBytes($num)
{
$suffix = "oct", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib"
$index = 0
while ($num -gt 1kb)
{
$num = $num / 1kb
$index++
}
$sFmt="{0:N"
if ($index -eq 0) {$sFmt += "0"} else {$sFmt += "1"}
$sFmt += "} {1}"
$sFmt -f $num, $suffix[$index]
}
Replace the block
# Create the formatted string expression.
$formatStr = "`"{0,5} {1,10} {2,5} {3,15:N0} ({4,11})" $formatStr += iif { -not $Q } { " {5}" } { " {5,-22} {6}" } $formatStr += "`" -f `$_.Mode," +
"`$_.$TimeField.ToString('d')," +
"`$_.$TimeField.ToString('t')," +
"`$_.Length,`$sfSize"
And
if (-not $Bare) {
$sfSize=DisplayInBytes $_.Length
invoke-expression $formatStr
And, at the end
# Output footer information when not using -bare.
if (-not $Bare) {
if (($fileCount -gt 0) -or ($dirCount -gt 0)) {
$sfSize = DisplayInBytes $sizeTotal
"{0,14:N0} file(s) {1,15:N0} ({3,11})`n{2,15:N0} dir(s)" -f
$fileCount,$sizeTotal,$dirCount,$sfSize
}
}
Upvotes: 1
Reputation: 68263
There are lots of ways to do this. Here's one:
switch -Regex ([math]::truncate([math]::log($bytecount,1024))) {
'^0' {"$bytecount Bytes"}
'^1' {"{0:n2} KB" -f ($bytecount / 1KB)}
'^2' {"{0:n2} MB" -f ($bytecount / 1MB)}
'^3' {"{0:n2} GB" -f ($bytecount / 1GB)}
'^4' {"{0:n2} TB" -f ($bytecount / 1TB)}
Default {"{0:n2} PB" -f ($bytecount / 1pb)}
}
Upvotes: 22
Reputation: 52420
Use a switch or a set of "if" statements. Your logic (pseudocode) should look like this:
Note that you should be testing in reverse order from the largest size to the smallest. Yes, I could have written the code for you, but I suspect you know enough to turn the above into a working script. It's just the approach that had you stumped.
Upvotes: 7
Reputation: 29450
An alternative to a bunch of if's/switches is to use a while loop until your value is the right size. It scales!
[double] $val = ($DirArray | Measure-Object -property length -sum).sum
while($val -gt 1kb){$val /= 1kb;}
"{0:N2}" -f $val
Upvotes: 0
Reputation: 6435
Mine is similar to @zdan 's one but written as a script function:
function DisplayInBytes($num)
{
$suffix = "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
$index = 0
while ($num -gt 1kb)
{
$num = $num / 1kb
$index++
}
"{0:N1} {1}" -f $num, $suffix[$index]
}
Upvotes: 24
Reputation: 763
I hope the following code will help you...
$file = 'C:\file.txt'
Write-Host((Get-Item $file).length/1KB) // returns file length in KB
Write-Host((Get-Item $file).length/1MB) // returns file length in MB
Write-Host((Get-Item $file).length/1GB) // returns file length in GB
Upvotes: 22
Reputation: 1868
Here a function that I wrote a while back that utilizes the Win32 API to accomplish what you are looking for.
Function Convert-Size {
<#
.SYSNOPSIS
Converts a size in bytes to its upper most value.
.DESCRIPTION
Converts a size in bytes to its upper most value.
.PARAMETER Size
The size in bytes to convert
.NOTES
Author: Boe Prox
Date Created: 22AUG2012
.EXAMPLE
Convert-Size -Size 568956
555 KB
Description
-----------
Converts the byte value 568956 to upper most value of 555 KB
.EXAMPLE
Get-ChildItem | ? {! $_.PSIsContainer} | Select -First 5 | Select Name, @{L='Size';E={$_ | Convert-Size}}
Name Size
---- ----
Data1.cap 14.4 MB
Data2.cap 12.5 MB
Image.iso 5.72 GB
Index.txt 23.9 KB
SomeSite.lnk 1.52 KB
SomeFile.ini 152 bytes
Description
-----------
Used with Get-ChildItem and custom formatting with Select-Object to list the uppermost size.
#>
[cmdletbinding()]
Param (
[parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias("Length")]
[int64]$Size
)
Begin {
If (-Not $ConvertSize) {
Write-Verbose ("Creating signature from Win32API")
$Signature = @"
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize( long fileSize, System.Text.StringBuilder buffer, int bufferSize );
"@
$Global:ConvertSize = Add-Type -Name SizeConverter -MemberDefinition $Signature -PassThru
}
Write-Verbose ("Building buffer for string")
$stringBuilder = New-Object Text.StringBuilder 1024
}
Process {
Write-Verbose ("Converting {0} to upper most size" -f $Size)
$ConvertSize::StrFormatByteSize( $Size, $stringBuilder, $stringBuilder.Capacity ) | Out-Null
$stringBuilder.ToString()
}
}
Upvotes: 10