【问题标题】:List all folders and subfolders in a given structure with filesize使用文件大小列出给定结构中的所有文件夹和子文件夹
【发布时间】:2019-06-14 19:00:43
【问题描述】:

我正在尝试列出光盘的文件夹结构和每个文件夹的大小。

我已经搞定了文件夹结构,现在我只需要输出每个文件夹的大小。

根据https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/dir,没有用于显示文件大小的标志 - 仅隐藏它。我猜我在这里走错了路,但感谢您提供任何帮助。

这是我目前所得到的:

dir /s /b /o:n /a:d > "C:\folderlist.txt"

预期输出:

C:\WINDOWS\system32\downlevel 400mb
C:\WINDOWS\system32\drivers 100mb
C:\WINDOWS\system32\DriverState 4kb
C:\WINDOWS\system32\DriverStore 1kb
C:\WINDOWS\system32\DRVSTORE 1gb

文件大小的首字母缩写词,即 (mb, kb, gb, tb) 无关紧要。只要它以某种可量化的方式显示文件夹大小。

也欢迎使用 Powershell 替代方案。

【问题讨论】:

    标签: windows powershell cmd command-line


    【解决方案1】:

    PowerShell 解决方案,基于 montonero's helpful answer 并改进了以下方面:

    • 控制递归深度
    • 提高性能
    • 与其他 cmdlet 更好地集成以实现可组合的功能

    示例调用,基于下面定义的函数Get-DirectorySize

    # Get the size of the current directory (only).
    Get-DirectorySize
    
    # As requested by the OP:
    # Recursively report the sizes of all subdirectories in the current directory.
    Get-DirectorySize -Recurse -ExcludeSelf
    
    # Get the size of all child directories and sort them by size, from largest
    # to smallest, showing only the 5 largest ones:
    Get-DirectorySize -Depth 1 -ExcludeSelf |
      Sort-Object Size -Descending |
        Select-Object -First 5
    

    上一条命令的示例输出:

    FullName                           FriendlySize       Size
    --------                           ------------       ----
    C:\Users\jdoe\AppData                3.27gb     3514782772
    C:\Users\jdoe\Desktop              801.40mb      840326199
    C:\Users\jdoe\.nuget               778.97mb      816814396
    C:\Users\jdoe\.vscode              449.12mb      470931418
    C:\Users\jdoe\Projects             104.07mb      109127742
    

    请注意,属性.FriendlySize 包含一个友好的、自动缩放的字符串 表示大小,而.Size 是一个包含实际字节数的数字([long]),这就是便于进一步的程序化处理。

    注意:为便于友好显示的输出对象添加属性只是为了实现方便。正确的 Powershell 方法是根据输出对象类型定义格式化指令 - 请参阅 the docs

    注意事项(也适用于链接的答案):

    • 仅报告逻辑大小,即文件数据所需的实际字节数,与磁盘大小不同,后者由于文件占用固定大小的块,通常更大;相反,压缩文件和稀疏文件占用更少磁盘空间。

    • 递归的实现(用-Recurse和/或-Depth)是低效的,因为遇到的每个目录的子树都被全扫描了;这在一定程度上得益于文件系统缓存。


    Get-DirectorySize源代码

    注意:需要 Windows PowerShell v3+;还兼容 PowerShell Core

    function Get-DirectorySize
    {
    
      param(
        [Parameter(ValueFromPipeline)] [Alias('PSPath')]
        [string] $LiteralPath = '.',
        [switch] $Recurse,
        [switch] $ExcludeSelf,
        [int] $Depth = -1,
        [int] $__ThisDepth = 0 # internal use only
      )
    
      process {
    
        # Resolve to a full filesystem path, if necessary
        $fullName = if ($__ThisDepth) { $LiteralPath } else { Convert-Path -ErrorAction Stop -LiteralPath $LiteralPath }
    
        if ($ExcludeSelf) { # Exclude the input dir. itself; implies -Recurse
    
          $Recurse = $True
          $ExcludeSelf = $False
    
        } else { # Process this dir.
    
          # Calculate this dir's total logical size.
          # Note: [System.IO.DirectoryInfo].EnumerateFiles() would be faster, 
          # but cannot handle inaccessible directories.
          $size = [Linq.Enumerable]::Sum(
            [long[]] (Get-ChildItem -Force -Recurse -File -LiteralPath $fullName).ForEach('Length')
          )
    
          # Create a friendly representation of the size.
          $decimalPlaces = 2
          $padWidth = 8
          $scaledSize = switch ([double] $size) {
            {$_ -ge 1tb } { $_ / 1tb; $suffix='tb'; break }
            {$_ -ge 1gb } { $_ / 1gb; $suffix='gb'; break }
            {$_ -ge 1mb } { $_ / 1mb; $suffix='mb'; break }
            {$_ -ge 1kb } { $_ / 1kb; $suffix='kb'; break }
            default       { $_; $suffix='b'; $decimalPlaces = 0; break }
          }
      
          # Construct and output an object representing the dir. at hand.
          [pscustomobject] @{
            FullName = $fullName
            FriendlySize = ("{0:N${decimalPlaces}}${suffix}" -f $scaledSize).PadLeft($padWidth, ' ')
            Size = $size
          }
    
        }
    
        # Recurse, if requested.
        if ($Recurse -or $Depth -ge 1) {
          if ($Depth -lt 0 -or (++$__ThisDepth) -le $Depth) {
            # Note: This top-down recursion is inefficient, because any given directory's
            #       subtree is processed in full.
            Get-ChildItem -Force -Directory -LiteralPath $fullName |
              ForEach-Object { Get-DirectorySize -LiteralPath $_.FullName -Recurse -Depth $Depth -__ThisDepth $__ThisDepth }
          }
        }
    
      }
    
    }
    

    这里是函数的基于评论的帮助;如果您将函数添加到 $PROFILE 中,请将帮助直接放在函数上方或放在函数主体内,以获得对 -? 的支持以及与 Get-Help 的自动集成。

    <#
    .SYNOPSIS
    Gets the logical size of directories in bytes.
    
    .DESCRIPTION
    Given a literal directory path, output that directory's logical size, i.e.,
    the sum of all files contained in the directory, including hidden ones.
    
    NOTE: 
    * The logical size is distinct from the size on disk, given that files
      are stored in fixed-size blocks. Furthermore, files can be compressed
      or sparse.
      Thus, the size of regular files on disk is typically greater than
      their logical size; conversely, compressed and sparse files require less
      disk space.
      Finally, the list of child items maintained by the filesystem for each 
      directory requires disk space too.
    
    * Wildcard expressions aren't directly supported, but you may pipe in
      Output from Get-ChildItem / Get-Item; if files rather than directotries 
      happen to be among the input objects, their size is reported as-is.
    
    CAVEATS:
     * Can take a long time to run with large directory trees, especially with
       -Recurse.
    * Recursion is implemented inefficently.
    
    .PARAMETER LiteralPath
    The literal path of a directory. May be provided via the pipeline.
    
    .PARAMETER Recurse
    Calculates the logical size not only of the input directory itself, but of
    all subdirectories in its subtree too.
    To limit the recursion depth, use -Depth.
    
    .PARAMETER Depth
    Limits the recursion depth to the specified number of levels. Implies -Recurse.
    Note that 0 means no recursion. Use just -Recurse in order not to limit the
    recursion.
    
    .PARAMETER ExcludeSelf
    Excludes the target directory itself from the size calculation.
    Implies -Recurse. Since -Depth implies -Recurse, you could use -ExcludeSelf
    -Depth 1 to report only the sizes of the immediate subdirectories.
    
    .OUTPUTS
    [pscustomobject] instances with properties FullName, Size, and FriendlySize.
    
    .EXAMPLE
    Get-DirectorySize
    
    Gets the logical size of the current directory.
    
    .EXAMPLE
    Get-DirectorySize -Recurse
    
    Gets the logical size of the current directory and all its subdirectories.
    
    .EXAMPLE
    Get-DirectorySize /path/to -ExcludeSelf -Depth 1 | Sort-Object Size
    
    Gets the logical size of all child directories in /path/to without including
    /path/to itself, and sorts the result by size (largest last).
    #>
    

    【讨论】:

      【解决方案2】:

      您无法使用dir 获取文件夹大小。您需要递归地单独计算每个文件夹的大小。有许多有效的 Powershell 示例。这个不错https://md3v.com/getting-a-folder-tree-size-with-powershell:

      function tree($startFolder)
      {
         $colItems = Get-ChildItem $startFolder | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
         foreach ($i in $colItems)
         {
             $subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
             $i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"
             tree $i.FullName
         }
      }
      

      【讨论】:

      • 干得好;为了保持一致性,我建议也将-Force 添加到Get-ChildItem $startFolder(包括隐藏目录)。 PSv3 将-Directory-File 开关引入Get-ChildItem,这使得Where-Object {$_.PSIsContainer -eq $true}Where-Object {$_.PSIsContainer -eq $false} 变得不必要。虽然没有这样记录,Get-ChildItem 在内部总是在输出之前按名称排序,因此不需要 Sort-Object 调用。
      • 感谢您的补充。我刚刚从md3v.com/getting-a-folder-tree-size-with-powershell 对示例进行了轻微修改,并且可以肯定它远非完美、快速和肮脏的解决方案。虽然它比你的小得多:)
      【解决方案3】:

      这是一个批处理脚本,列出了给定根目录中的所有目录及其大小。提供根目录作为第一个命令行参数;如果省略,则使用当前目录。这是代码:

      @echo off
      setlocal EnableExtensions DisableDelayedExpansion
      
      rem // Define constants here:
      set "_ROOT=%~1" & if not defined _ROOT set "_ROOT=."
      
      rem // Change into the given root directory:
      pushd "%_ROOT%" && (
          rem // Walk through all immediate sub-directories:
          for /F "delims= eol=|" %%D in ('dir /B /A:D-H "*"') do (
              rem // Initialise variable holding size of sub-directory:
              set "SIZE=0"
              rem // Process sub-directory in a sub-soutine:
              set "ITEM=%%~D" & call :PROCESS SIZE "%%ITEM%%"
              rem // Display size of sub-directory:
              call set "SIZE=              %%SIZE%%"
              set "ITEM=%%~fD" & call echo %%SIZE:~-14%%  "%%ITEM%%"
          )
          popd
      )
      
      endlocal
      exit /B
      
      
      :PROCESS
          rem /* Change into the given directory; use short names to avoid trouble with
          rem    extremely long and/or deep paths (remember the limit is 260 characters): */
          pushd "%~s2" && (
              rem /* Walk through all files in the directory; instead of a normal `for` loop,
              rem    `dir` together with a `for /F` loop is used, because `for` would ignore
              rem    hidden files; with `dir` you can choose the attributes and therefore
              rem    ensure that all files are returned: */
              for /F "delims= eol=|" %%F in ('2^> nul dir /B /A:-D "*"') do (
                  rem // Sum up the file sizes in a sub-routine:
                  call :SUM SIZE "%%SIZE%%" "%%~zF"
              )
              rem /* Walk through all sub-directories; instead of a normal `for` loop, `dir`
              rem    together with a `for /F` loop is used, because `for` would ignore hidden
              rem    sub-directories; with `dir` you can choose the attributes and therefore
              rem    ensure that all sub-directories are returned: */
              for /F "delims= eol=|" %%D in ('2^> nul dir /B /A:D "*"') do (
                  rem // Call this sub-routine recursively to process sub-directories:
                  set "ITEM=%%~D" & call :PROCESS SIZE "%%ITEM%%"
              )
              popd
          )
          rem // Return resulting directory size:
          set "%~1=%SIZE%"
          exit /B
      
      :SUM
          rem // Split provided numbers into ones and billions:
          set "ENA1=%~2" & set "ENA2=%~3"
          set "GIG1=%ENA1:~,-9%" & set "ENA1=%ENA1:~-9%"
          set "GIG2=%ENA2:~,-9%" & set "ENA2=%ENA2:~-9%"
          rem /* Sum up the ones, disregard leading zeros, which would let numbers be
          rem    interpreted as octal ones: */
          for /F "tokens=* delims=0" %%M in ("0%ENA1%") do (
              for /F "tokens=* delims=0" %%N in ("0%ENA2%") do (
                  set /A "ENA=%%M+%%N+0"
              )
          )
          rem // Sum up the billions, regard the carry from the ones:
          set "GIG=%ENA:~,-9%" & set /A "GIG1+=0, GIG2+=0, GIG+=GIG1+GIG2"
          rem // Join resulting billions and ones to the finally resulting number:
          set "ENA=000000000%ENA%"
          for /F "tokens=* delims=0" %%K in ("%GIG%%ENA:~-9%") do set "%~1=%%K"
          exit /B
      

      调用示例(假设脚本名为list_folders.bat):

      list_folders.bat "D:\Root"
      

      示例输出:

              442368  "D:\Root\Data"
           101685022  "D:\Root\More"
             5441536  "D:\Root\Test"
      

      【讨论】:

        猜你喜欢
        • 2020-07-02
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 2011-10-30
        • 2016-02-26
        • 2018-08-06
        • 2013-05-01
        • 1970-01-01
        相关资源
        最近更新 更多