37

How can I modify the default ls (Get-ChildItem) in PowerShell so that it displays human-readable file sizes, like ls -h on a *nix machine?

ls -lh does simple logic with the file size, so that it shows bytes for really small files, kilobytes for files over 1K (with one decimal place if it's under 10K), and megabytes for files over 1M (with one decimal place if it's under 10MB).

16

try this

PS> gc c:\scripts\type\shrf.ps1xml

<Types>
<Type>
  <Name>System.IO.FileInfo</Name>
   <Members>
      <ScriptProperty>
          <Name>FileSize</Name>
          <GetScriptBlock>
             switch($this.length) {
               { $_ -gt 1tb } 
                      { "{0:n2} TB" -f ($_ / 1tb) }
               { $_ -gt 1gb } 
                      { "{0:n2} GB" -f ($_ / 1gb) }
               { $_ -gt 1mb } 
                      { "{0:n2} MB " -f ($_ / 1mb) }
               { $_ -gt 1kb } 
                      { "{0:n2} KB " -f ($_ / 1Kb) }
               default  
                      { "{0} B " -f $_} 
             }      
          </GetScriptBlock>
     </ScriptProperty>   
  </Members>
</Type>
</Types>

PS> Update-TypeData -AppendPath c:\scripts\type\shrf.ps1xml -verbose
PS> get-childItem $env:windir  | select Name,FileSize,length
PS> # you can paste this in your profile
PS> 

you can also use dynamic type data with PS3:

   PS> Update-TypeData -TypeName System.IO.FileInfo -MemberName FileSize -MemberType ScriptProperty -Value { 

    switch($this.length) {
               { $_ -gt 1tb } 
                      { "{0:n2} TB" -f ($_ / 1tb) }
               { $_ -gt 1gb } 
                      { "{0:n2} GB" -f ($_ / 1gb) }
               { $_ -gt 1mb } 
                      { "{0:n2} MB " -f ($_ / 1mb) }
               { $_ -gt 1kb } 
                      { "{0:n2} KB " -f ($_ / 1Kb) }
               default  
                      { "{0} B " -f $_} 
             }      

 } -DefaultDisplayPropertySet Mode,LastWriteTime,FileSize,Name
6
  • 1
    I really like building it in as an extra property. The only problem using the PS3 version is I get: Update-TypeData : Error in TypeData "System.IO.FileInfo": The member DefaultDisplayPropertySet is already present. Running latest PS3 full release from 9/4. Sep 5 '12 at 15:24
  • 4
    Great answer! It's hard to believe there isn't a switch for Get-ChildItem that would just do this out of the box Jul 30 '15 at 15:34
  • 1
    Great answer but has anyone got -DefaultDisplayPropertySet to work?
    – Nick Cox
    Dec 23 '17 at 3:04
  • Doesn't work like @ThomasG.Mayfield said.
    – Andria
    Nov 22 '18 at 5:01
  • 1
    Nice answer but once again I find it hilarious how much Powershell code you have to write to accomplish something as simple as -h
    – Elliott B
    Mar 31 at 2:59
17

First, create the following function:

Function Format-FileSize() {
    Param ([int64]$size)
    If     ($size -gt 1TB) {[string]::Format("{0:0.00} TB", $size / 1TB)}
    ElseIf ($size -gt 1GB) {[string]::Format("{0:0.00} GB", $size / 1GB)}
    ElseIf ($size -gt 1MB) {[string]::Format("{0:0.00} MB", $size / 1MB)}
    ElseIf ($size -gt 1KB) {[string]::Format("{0:0.00} kB", $size / 1KB)}
    ElseIf ($size -gt 0)   {[string]::Format("{0:0.00} B", $size)}
    Else                   {""}
}

You can then pipe the output of Get-ChildItem through Select-Object and use a calculated property to format the filesize:

Get-ChildItem | Select-Object Name, @{Name="Size";Expression={Format-FileSize($_.Length)}}

The function could of course be improved to account for sizes in the PB range and more, or to vary the number of decimal points as necessary.

5
  • Is there a reason that an alias can't be created to do this as getshilditem itself (check is there is an -lh flag or something, and if not, then just use Get-ChildItem, else use this)
    – soandos
    Aug 31 '12 at 20:05
  • You can't create aliases for piped commands, nor override the default aliases. If you can live with using an alias like ls2, try creating another function that does the logic you described based on a parameter, then add an alias for it. See here for more information on creating aliases.
    – Indrek
    Aug 31 '12 at 20:24
  • Alternatively, look into custom formatting files to extend cmdlet output. See this forum topic for an example. Also, to make the formatting function persist through PowerShell sessions, add it to your profile file (see Get-Variable profile for its location).
    – Indrek
    Aug 31 '12 at 20:31
  • 2
    For me, this function doesn't work files larger than ~2GB, as $size is defined as an int, which is a int32. For this to work with large files, define $size as int64 or uint64.
    – Alex Leach
    Jul 1 '19 at 11:17
  • I get Select-Object : Es wurde kein Positionsparameter gefunden, der das Argument "System.Collections.Hashtable" akzeptiert.. How can I specify the path? I use $pst= Get-ChildItem -Path $home_user -Filter *.pst -Recurse -File| Sort-Object Length -Descending | ForEach-Object{ $_.FullName}. This works, but without file sizes.
    – Timo
    Oct 28 '19 at 9:52
13

Something like the following for listing just file sizes. Yes it is a bit sore on the eyes but it manages to get the job done.

For converting to KB:

ls | Select-Object Name, @{Name="KiloBytes";Expression={$_.Length / 1KB}}

For converting to MB:

ls | Select-Object Name, @{Name="MegaBytes";Expression={$_.Length / 1MB}}
2
  • 1
    Best answer, without a script/function. The pipe solution!
    – Timo
    Aug 8 '19 at 7:03
  • To make it more readable (less sore on eye), I add some whitespace before the number with this: ls | Select-Object Name, @{Name="-------------MegaBytes";Expression={$_.Length / 1MB}} You can also use spaces (dashes here for clarity).
    – wistlo
    Feb 29 '20 at 19:20
6

based on the answer by walid toumi:

Steps to do:

  • Create your own Type-file with the new FileSize-Property
  • Change the standard output format for FileInfo
  • load the changes in $PROFILE

Create your own Type-file with the new FileSize-Property

  • Create your own Type-file: MyTypes.ps1xml
    (I put it in $Env:USERPROFILE\Documents\WindowsPowershell, so right next to my $PROFILE)

    <?xml version="1.0" encoding="utf-8" ?>
    <Types>
        <Type>
            <Name>System.IO.FileInfo</Name>
            <Members>
                <ScriptProperty>
                    <!-- Filesize converts the length to a human readable
                        format (kb, mb, gb, tb) -->
                    <Name>FileSize</Name>
                    <GetScriptBlock>
                        switch($this.length) {
                            { $_ -gt 1tb } 
                                { "{0:n2} TB" -f ($_ / 1tb) ; break }
                            { $_ -gt 1gb } 
                                { "{0:n2} GB" -f ($_ / 1gb) ; break }
                            { $_ -gt 1mb } 
                                { "{0:n2} MB " -f ($_ / 1mb) ; break }
                            { $_ -gt 1kb } 
                                { "{0:n2} KB " -f ($_ / 1Kb) ; break }
                            default
                                { "{0}  B " -f $_}
                        }
                    </GetScriptBlock>
                </ScriptProperty>
            </Members>
        </Type>
    </Types>
    
  • load the new property in a powershell-session:

    • Update-TypeData -PrependPath $Env:USERPROFILE\Documents\WindowsPowershell\MyTypes.ps1xml
  • try the new property
    • Get-ChildItem | Format-Table -Property Name, Length, FileSize

Change the standard output format for FileInfo

  • create your own Fileformat-file: MyFileFormat.format.ps1xml (Again in $Env:USERPROFILE\Documents\WindowsPowershell\)

    <?xml version="1.0" encoding="utf-8" ?> 
    <Configuration>
        <SelectionSets>
            <SelectionSet>
                <Name>FileSystemTypes</Name>
                <Types>
                    <TypeName>System.IO.DirectoryInfo</TypeName>
                    <TypeName>System.IO.FileInfo</TypeName>
                </Types>
            </SelectionSet>
        </SelectionSets>
    
        <!-- ################ GLOBAL CONTROL DEFINITIONS ################ -->
        <Controls>
            <Control>
                <Name>FileSystemTypes-GroupingFormat</Name>
                        <CustomControl>
                            <CustomEntries>
                                <CustomEntry>
                                    <CustomItem>
                                        <Frame>
                                            <LeftIndent>4</LeftIndent>
                                            <CustomItem>
                                                <Text AssemblyName="System.Management.Automation" BaseName="FileSystemProviderStrings" ResourceId="DirectoryDisplayGrouping"/>
                                                <ExpressionBinding>
                                                  <ScriptBlock>
                                                      $_.PSParentPath.Replace("Microsoft.PowerShell.Core\FileSystem::", "")                                                  
                                                  </ScriptBlock>
                                                </ExpressionBinding>
                                                <NewLine/>
                                            </CustomItem> 
                                        </Frame>
                                    </CustomItem>
                                </CustomEntry>
                            </CustomEntries>
                </CustomControl>
            </Control>
        </Controls>
    
        <!-- ################ VIEW DEFINITIONS ################ -->
    
        <ViewDefinitions>
           <View>
                <Name>children</Name>
                <ViewSelectedBy>
                    <SelectionSetName>FileSystemTypes</SelectionSetName>
                </ViewSelectedBy>
                <GroupBy>
                    <PropertyName>PSParentPath</PropertyName> 
                    <CustomControlName>FileSystemTypes-GroupingFormat</CustomControlName>  
                </GroupBy>
                <TableControl>
                    <TableHeaders>
                       <TableColumnHeader>
                          <Label>Mode</Label>
                          <Width>7</Width>
                          <Alignment>left</Alignment>
                       </TableColumnHeader>
                        <TableColumnHeader>
                            <Label>LastWriteTime</Label>
                            <Width>25</Width>
                            <Alignment>right</Alignment>
                        </TableColumnHeader>
                        <TableColumnHeader>
                            <Label>FileSize</Label>
                            <Width>14</Width>
                            <Alignment>right</Alignment>
                        </TableColumnHeader>
                        <TableColumnHeader/>
                    </TableHeaders>
                    <TableRowEntries>
                        <TableRowEntry>
                            <Wrap/>
                            <TableColumnItems>
                                <TableColumnItem>
                                    <PropertyName>Mode</PropertyName>
                                </TableColumnItem>
                                <TableColumnItem>
                                    <ScriptBlock>
                                        [String]::Format("{0,10}  {1,8}", $_.LastWriteTime.ToString("d"), $_.LastWriteTime.ToString("t"))
                                    </ScriptBlock>
                                </TableColumnItem>
                                <TableColumnItem>
                                <PropertyName>FileSize</PropertyName>
                                </TableColumnItem>
                                <TableColumnItem>
                                    <PropertyName>Name</PropertyName>
                                </TableColumnItem>
                            </TableColumnItems>
                        </TableRowEntry>
                    </TableRowEntries>
                </TableControl>
            </View>
            <View>
                <Name>children</Name>
                <ViewSelectedBy>
                    <SelectionSetName>FileSystemTypes</SelectionSetName>
                </ViewSelectedBy>
                <GroupBy>
                    <PropertyName>PSParentPath</PropertyName> 
                    <CustomControlName>FileSystemTypes-GroupingFormat</CustomControlName>  
                </GroupBy>
                <ListControl>
                    <ListEntries>
                        <ListEntry>
                            <EntrySelectedBy>
                                <TypeName>System.IO.FileInfo</TypeName>
                            </EntrySelectedBy>
                            <ListItems>
                                <ListItem>
                                    <PropertyName>Name</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>FileSize</PropertyName>
                                </ListItem>
                               <ListItem>
                                    <PropertyName>CreationTime</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>LastWriteTime</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>LastAccessTime</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>Mode</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>LinkType</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>Target</PropertyName>
                                </ListItem>                        
                                <ListItem>
                                    <PropertyName>VersionInfo</PropertyName>
                                </ListItem>
                            </ListItems>
                        </ListEntry>
                        <ListEntry>
                            <ListItems>
                                <ListItem>
                                    <PropertyName>Name</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>CreationTime</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>LastWriteTime</PropertyName>
                                </ListItem>
                                <ListItem>
                                    <PropertyName>LastAccessTime</PropertyName>
                                </ListItem>
                              <ListItem>
                                <PropertyName>Mode</PropertyName>
                              </ListItem>
                              <ListItem>
                                <PropertyName>LinkType</PropertyName>
                              </ListItem>
                              <ListItem>
                                <PropertyName>Target</PropertyName>
                              </ListItem>
                            </ListItems>
                        </ListEntry>
                    </ListEntries>
                </ListControl>
            </View>
            <View>
                <Name>children</Name>
                <ViewSelectedBy>
                    <SelectionSetName>FileSystemTypes</SelectionSetName>
                </ViewSelectedBy>
                <GroupBy>
                    <PropertyName>PSParentPath</PropertyName> 
                    <CustomControlName>FileSystemTypes-GroupingFormat</CustomControlName>  
                </GroupBy>
                <WideControl>
                    <WideEntries>
                        <WideEntry>
                            <WideItem>
                                <PropertyName>Name</PropertyName>
                            </WideItem>
                        </WideEntry>
                        <WideEntry>
                            <EntrySelectedBy>
                                <TypeName>System.IO.DirectoryInfo</TypeName>
                            </EntrySelectedBy>
                            <WideItem>
                                <PropertyName>Name</PropertyName>
                                <FormatString>[{0}]</FormatString>
                            </WideItem>
                        </WideEntry>
                    </WideEntries>
                </WideControl>
            </View>
            <View>
                <Name>FileSecurityTable</Name>
                <ViewSelectedBy>
                    <TypeName>System.Security.AccessControl.FileSystemSecurity</TypeName>
                </ViewSelectedBy>
                <GroupBy>
                    <PropertyName>PSParentPath</PropertyName> 
                    <CustomControlName>FileSystemTypes-GroupingFormat</CustomControlName>  
                </GroupBy>
                <TableControl>
                    <TableHeaders>
                       <TableColumnHeader>
                          <Label>Path</Label>
                       </TableColumnHeader>
                       <TableColumnHeader />
                       <TableColumnHeader>
                          <Label>Access</Label>
                       </TableColumnHeader>
                    </TableHeaders>
                    <TableRowEntries>
                        <TableRowEntry>
                            <TableColumnItems>
                                <TableColumnItem>
                                    <ScriptBlock>
                                        split-path $_.Path -leaf
                                    </ScriptBlock>
                                </TableColumnItem>
                                <TableColumnItem>
                                <PropertyName>Owner</PropertyName>
                                </TableColumnItem>
                                <TableColumnItem>
                                    <ScriptBlock>
                                        $_.AccessToString
                                    </ScriptBlock>
                                </TableColumnItem>
                            </TableColumnItems>
                        </TableRowEntry>
                    </TableRowEntries>
                </TableControl>
            </View>
           <View>
                <Name>FileSystemStream</Name>
                <ViewSelectedBy>
                    <TypeName>Microsoft.PowerShell.Commands.AlternateStreamData</TypeName>
                </ViewSelectedBy>
                <GroupBy>
                    <PropertyName>Filename</PropertyName> 
                </GroupBy>
                <TableControl>
                    <TableHeaders>
                       <TableColumnHeader>
                          <Width>20</Width>
                          <Alignment>left</Alignment>
                       </TableColumnHeader>
                        <TableColumnHeader>
                            <Width>10</Width>
                            <Alignment>right</Alignment>
                        </TableColumnHeader>
                    </TableHeaders>
                    <TableRowEntries>
                        <TableRowEntry>
                            <TableColumnItems>
                                <TableColumnItem>
                                    <PropertyName>Stream</PropertyName>
                                </TableColumnItem>
                                <TableColumnItem>
                                    <PropertyName>Length</PropertyName>
                                </TableColumnItem>
                            </TableColumnItems>
                        </TableRowEntry>
                    </TableRowEntries>
                </TableControl>
            </View>          
        </ViewDefinitions>
    </Configuration>
    

    (It's allmost a direct copy of the original $PSHOME\FileFormat.format.ps1xml. I only changed Length to FileSize a few times)

  • load the new format in our powershell session:

    • Update-FormatData -PrependPath $Env:USERPROFILE\Documents\WindowsPowershell\MyFileFormat.format.ps1xml
  • try the new property
    • Get-ChildItem

load the changes in $PROFILE

  • copy these lines to $PROFILE to load the changes in every new session

    # local path to use in this script
    $scriptpath = Split-Path -parent $MyInvocation.MyCommand.Definition
    
    # custom types and formats
    # currently only System.IO.FileInfo is changed
    update-TypeData -PrependPath $scriptpath\MyTypes.ps1xml
    update-FormatData -PrependPath $scriptpath\MyFileFormat.format.ps1xml
    
4

Here's a bit of an orthogonal answer to this quite-old question. It's not an answer to the question as literally asked, but more about the "spirit" of the question. It's also not an answer that was even possible for years after the question was asked. But now, it may be useful for some subset of folks who come looking for this information (as I did today).

Since the original question pointed out how easy this is in *nix, I realized that with WSL (Windows Subsystem for Linux) installed, it's a no-brainer to just use the Linux ls -lh from PowerShell:

PS> wsl ls -lh

... gives me the results I want (and it sounds like the OP did) on the current directory from within PowerShell. Better yet, if you have exa installed in your WSL instance, wsl exa -l is much prettier (and defaults to -h).

Note that if you want a different directory, there's a bit of a "trick" when using the wsl command. Since utilities running inside a WSL instance (such as ls) only understand Linux paths, you can't do something like:

PS> wsl ls -lh C:\

Instead, there are two options. First, you can use the WSL equivalent of the Windows path. E.g.:

PS> wsl ls -lh /mnt/c
PS> wsl exa -l /mnt/c

Or, just set the directory for the WSL instance with the --cd option, like so:

PS> wsl --cd C:\ ls -lh # or
PS> wsl --cd C:\ exa -l

This also works with relative directory paths:

PS> wsl --cd .. ls -lh # or
PS> wsl --cd .. exa -l

Don't worry (if you were) -- it's not going to change the current directory in PowerShell. When the WSL instance exits after running ls command, PowerShell will retain its current environment; just like running a subshell (in either PowerShell or a Linux shell like Bash).

PowerShell and WSL are each great on their own, but putting the two together gives you something even more powerful.

P.S. This works with either WSL1 or WSL2, but if you are going to be handling a lot of Windows files in WSL, version 1 is (currently) an order of magnitude faster. WSL2 is faster when using the virtualized ext4 filesystem.

1
  • 1
    Wow, II did not know you could run WSL in PowerShell. No more verbose PS BS. Thanks for this!
    – Rob
    Aug 4 at 7:44
2

I used jmreicha's solution with an alias in my $profile:

function Get-ChildItem-MegaBytes {
  ls $args | Select-Object Name, @{Name="MegaBytes";Expression={$_.Length / 1MB}}
}

Set-Alias -name megs -val Get-ChildItem-MegaBytes

Now I just type: megs [whatever]

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.