4

I have the following

Import-Module SqlServer

$Analysis_Server = New-Object Microsoft.AnalysisServices.Server  
$Analysis_Server.connect("$server")

$estimatedSize = $([math]::Round($($($Analysis_Server.Databases[$cube].EstimatedSize)/1024/1024),2))

this generates for me the size in MB

I would like to enhance this to make it more user friendly readable especially for values that are in the double digit MB

for example, sometimes i get values 50.9 MB which is good. but some other values are 37091 MB or 3082.86 MB, and i'd like values like that to be automatically converted to GB (37.09 GB, 3.08 GB respectively) if they are in the GB range.

and if there are values that are not in MB range, they should be displayed in KB

i.e. 0.78 MB should just be 780 KB

how can i accomplish this?

Cataster
  • 3,081
  • 5
  • 32
  • 79

4 Answers4

5

Here's two more ways of formatting a size in bytes:

1) Using a switch()

function Format-Size() {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [double]$SizeInBytes
    )
    switch ([math]::Max($SizeInBytes, 0)) {
        {$_ -ge 1PB} {"{0:N2} PB" -f ($SizeInBytes / 1PB); break}
        {$_ -ge 1TB} {"{0:N2} TB" -f ($SizeInBytes / 1TB); break}
        {$_ -ge 1GB} {"{0:N2} GB" -f ($SizeInBytes / 1GB); break}
        {$_ -ge 1MB} {"{0:N2} MB" -f ($SizeInBytes / 1MB); break}
        {$_ -ge 1KB} {"{0:N2} KB" -f ($SizeInBytes / 1KB); break}
        default {"$SizeInBytes Bytes"}
    }
}

2) By performing a loop where the gives byte size is repeatedly divided by 1024

function Format-Size2 {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [double]$SizeInBytes
    )

    $units = "Bytes", "KB", "MB", "GB", "TB", "PB", "EB"
    $index = 0

    while ($SizeInBytes -gt 1024 -and $index -le $units.length) {
        $SizeInBytes /= 1024
        $index++
    }
    if ($index) {
        return '{0:N2} {1}' -f $SizeInBytes, $units[$index]
    }

    return "$SizeInBytes Bytes"
}
Theo
  • 57,719
  • 8
  • 24
  • 41
  • Question, what does n2 mean in 0:N2 ? Also, what is the 0 for in [math]::Max($SizeInBytes, 0) – Cataster Aug 17 '19 at 13:51
  • @Cataster The `N2` is to [format](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings) the number to have two decimals. The `[Math]::Max()` I did to ensure the switch handles a positive number only. – Theo Aug 17 '19 at 20:28
4

Leaving the KB: 1024 vs 1000 discussion aside as we should use KiB but nobody does, including Microsoft (PowerShell, Explorer, etc.):

PS C:\> 1Kb
1024

Using the The Windows Shell shlwapi.h StrFormatByteSize function:

$Shlwapi = Add-Type -MemberDefinition '
    [DllImport("Shlwapi.dll", CharSet=CharSet.Auto)]public static extern int StrFormatByteSize(long fileSize, System.Text.StringBuilder pwszBuff, int cchBuff);
' -Name "ShlwapiFunctions" -namespace ShlwapiFunctions -PassThru

Function Format-ByteSize([Long]$Size) {
    $Bytes = New-Object Text.StringBuilder 20
    $Return = $Shlwapi::StrFormatByteSize($Size, $Bytes, $Bytes.Capacity)
    If ($Return) {$Bytes.ToString()}
}

Examples:

PS C:\> Format-ByteSize 37091MB
36.2 GB
PS C:\> Format-ByteSize 3082.86MB
3.00 GB
PS C:\> Format-ByteSize 0.78MB
798 KB
PS C:\> Format-ByteSize 670
670 bytes
iRon
  • 20,463
  • 10
  • 53
  • 79
  • Interesting! Can.you explain this part? `$Bytes = New-Object Text.StringBuilder 20` what does 20 do? Also, I know in my post I said MB to KB or GB (or GiB/KiB since thts more correct), but your function works for a starting size in bytes correct? In other words, I should get the default size in bytes: Get the size in bytes for this function `$Size = $Analysis_Server.Databases[$cube].EstimatedSize` oh wait just realized you are passing integer literal along with the number. Hmm, the output of $Size in this case has to be accompanied with an integer literal, I.e. ($Size)B – Cataster Aug 17 '19 at 13:42
  • Yes, PowerShell automatically parses values like `37091MB` to an integer. `20` is the capacity (the number of characters) the String builder will hold, see also: https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder?view=netframework-4.8 – iRon Aug 17 '19 at 14:06
  • the input, $Size, must also be accompanied with an integer literal, correct? So i have to.append B to this `$Size = $Analysis_Server.Databases[$cube].EstimatedSize` – Cataster Aug 17 '19 at 14:21
  • Well in my case, the value is generated without an integer literal, so for example 670. But 670 is in bytes. How can I append to the input this unit? I.e. `Format-Bytes ($Size -append 'B'))` – Cataster Aug 17 '19 at 15:22
1

I believe this is a simple implementation of a conditional, control, statement like IF().

Powershell Basics: IF Statements

Example Psuedo Code:

If ($estimatedSize -lt 1)  {

 $newKbEstimateSize = $estimatedSize *  1000; //MB to KB

}

If ($estimatedSize  -gt 999.9)  {

  $newGbEstimatedSize = $estimatedSize / 1000; //MB to GB

}
Matthew E. Miller
  • 557
  • 1
  • 5
  • 13
  • 1
    This is technically correct. However, I believe the asker wants the value in Gibibytes (GiB) and Kibibytes (KiB), which require you to divide or multiply by 1024 respectively – spicy.dll Aug 16 '19 at 20:29
  • @MasonSchmidgall No i think Mathew got it correct, unless im confusing the KB with KiB? and GB with GiB? im just looking for the everyday unit that people know about , MB/GB ... is there a difference between the two? i never heard of Kibi or Gibi...and if thats the case, then would the conditionals also have to be updated from 1 to something else? and 999.9 to 1023.9? – Cataster Aug 16 '19 at 20:41
  • @Cataster 1 MiB = 1024 KiB and 1 MB = 1000 KB. It's confusing because Windows refers to MiB as MB when it's actually not. – spicy.dll Aug 16 '19 at 20:44
  • @MasonSchmidgall woah...maybe this is incorrect afterall...if i make them 1024, what should i fix the conditional statements into? do i change the 1 and the 999.9? – Cataster Aug 16 '19 at 20:46
  • @Cataster you would only change `$newKbEstimateSize = $estimatedSize * 1024`, as well as the other one to be divided by that. Also note that semicolons are not required in PowerShell unless you need to put multiple separate commands on the same line – spicy.dll Aug 16 '19 at 20:49
  • 1
    Also realized that this isn't PowerShell. Will post an accurate answer – spicy.dll Aug 16 '19 at 20:51
  • @MasonSchmidgall you have a good eye for detail, I glanced over the use of 1024 in the original question. Thank you for building upon my solution. Your answer is more usable and accurate. – Matthew E. Miller Aug 17 '19 at 02:11
1

I would use the following code:

Import-Module SqlServer

$Analysis_Server = New-Object Microsoft.AnalysisServices.Server  
$Analysis_Server.connect("$server")

# Get the size in bytes
$estimatedSize = $Analysis_Server.Databases[$cube].EstimatedSize

$convertFactor = 1kb

# Check if >= 1gib
if ($estimatedSize -ge 1gb) {
    # Will convert to gibibytes
    $convertFactor = 1gb
}
elseif ($estimatedSize -ge 1mb) {
    # Will convert to mibibytes
    $convertFactor = 1mb
}

# Number of bytes / Number of Bytes in unit
$estimatedSize = [math]::Round($estimatedSize / $convertFactor, 2)

The above takes the amount in bytes and compares it using the numeric multipliers for data storage. If the amount in bytes is greater, it uses that conversion. Otherwise, it just uses Kibibytes

spicy.dll
  • 948
  • 8
  • 23