1

我是 PowerShell 的相对新手,想了解更多关于函数、CmdLet 和人类可读值的信息。通常是为了学习新事物,看看别人做得很好。

所以我去找了一个Get-DiskUsage CmdLet 的源代码。

我可以 。将其输入 PowerShell,然后调用该函数。

不知何故,无论我如何称呼它,它总是使用当前目录来获取结果。

我错过了什么,它不会采用给定的 -Path 参数?

PS D:\bin> . .\DiskUsage.ps1
PS D:\bin> Get-DiskUsage -Path D:\ -h

Size Folder
---- ------
405M


PS D:\bin> Get-DiskUsage -Path C:\ -h

Size Folder
---- ------
405M


PS D:\bin> C:
PS C:\> Get-DiskUsage -Path C:\ -h

Size Folder
---- ------
 18G


PS C:\>

脚本输出不正确,因为这是SysInternals DiskUsage 工具du显示的内容:

D:\bin>du C:\

Du v1.4 - report directory disk usage
Copyright (C) 2005-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

Files:        93367
Directories:  22541
Size:         21.817.875.778 bytes
Size on disk: 22.127.992.832 bytes


D:\bin>du D:\

Du v1.4 - report directory disk usage
Copyright (C) 2005-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

Files:        132832
Directories:  15125
Size:         130.137.231.457 bytes
Size on disk: 54.992.396.288 bytes


D:\bin>du D:\bin

Du v1.4 - report directory disk usage
Copyright (C) 2005-2011 Mark Russinovich
Sysinternals - www.sysinternals.com

Files:        3118
Directories:  222
Size:         424.866.944 bytes
Size on disk: 288.858.112 bytes
4

3 回答 3

4
Get-DiscUsage C:\ # NOT RECOMMENDED

Get-DiskUsage -Path C:\Users\JBennett\SkyDrive\WindowsPowershell\Scripts\IO\

Size Folder
---- ------
223424B C:\Users\JBennett\SkyDrive\WindowsPowershell\Scripts\IO\DU

无论哪种方式都对我有用(如您所说,在第一次点源脚本之后)。

我不得不说,我不会使用它......它正在使用 PowerShell 进行递归目录扫描,生成(数千个)DirectoryInfo 对象,然后在每个文件夹上进行另一个递归目录扫描,创建一个新对象并汇总属性... 与 du.exe 相比,它非常慢,并且会消耗大量内存和 IO

稍微好一点的方法

当您希望递归地获取树中每个文件夹的大小时,算法上正确的方法不涉及 Get-ChildItem 上的 -Recurse 标志。你可能会做这样的事情:

function Get-Size {
  #.Synopsis
  #  Calculate the size of a folder on disk
  #.Description
  #  Recursively calculate the size of a folder on disk,
  #  outputting it's size, and that of all it's children,
  #  and optionally, all of their children
  param(
    [string]$root,
    # Show the size for each descendant recursively (otherwise, only immediate children)
    [switch]$recurse
  )
  # Get the full canonical FileSystem path:
  $root = Convert-Path $root

  $size = 0
  $files = 0
  $folders = 0

  $items = Get-ChildItem $root
  foreach($item in $items) {
    if($item.PSIsContainer) {
      # Call myself recursively to calculate subfolder size
      # Since we're streaming output as we go, 
      #   we only use the last output of a recursive call
      #   because that's the summary object
      if($recurse) {
        Get-Size $item.FullName | Tee-Object -Variable subItems
        $subItem = $subItems[-1]
      } else {
        $subItem = Get-Size $item.FullName | Select -Last 1
      }

      # The (recursive) size of all subfolders gets added
      $size += $subItem.Size
      $folders += $subItem.Folders + 1
      $files += $subItem.Files
      Write-Output $subItem
    } else {
      $files += 1
      $size += $item.Length
    }
  }

  # in PS3, use the CustomObject trick to control the output order
  if($PSVersionTable.PSVersion -ge "3.0") {
    [PSCustomObject]@{ 
      Folders = $folders
      Files = $Files
      Size = $size
      Name = $root
    }
  } else {
    New-Object PSObject -Property @{ 
      Folders = $folders
      Files = $Files
      Size = $size
      Name = $root
    }
  }
}

这样,您就不会多次列出任何内容。不利的一面是您必须自己添加所有内容,因为您不能只使用 Measure-Object。

OP 中引用的脚本实际上列出了每个项目,无论它有多深。也就是说,如果文件位于 C:\Users\Jaykul\Documents\WindowsPowerShell\Scripts ... 中,它将显示在gci -recurse每个父文件夹中 - 如果您在根目录上调用脚本,则会出现五次。

当然,您仍在为每个文件创建一个 FileInfo,为每个目录创建一个 DirectoryInfo,加上我在脚本中手动创建的摘要 PSObject,所以即使这几乎与 du.exe 一样快,它也会有更多对记忆的影响。

然而,这是 PowerShell 中固​​有的权衡:做事更容易……因为我们有很重的对象。我们很乐意用易用性换取内存,有时甚至会影响性能。也就是说:是的,du.exe 会更快,但它是一个必须有人为一个目的而专门编写的一招一式的小马。如果您只想运行它并读取输出,这仍然是最好的方法。但是如果你想在脚本中使用它的输出,它会产生你必须解析的文本输出,而且很难收集这些信息作为整体仪表板式监控脚本输出的一部分......

于 2013-04-25T00:52:38.623 回答
2

我喜欢 Jaykul 的回答,但它在 PS 2.0 中有一个错误,当目录为空时会返回错误的答案。我还对其进行了修改,以支持强制开关和深度开关,而不是递归,类似于原始的 Get-DiskUsage 脚本。

function Get-Size {
    #.Synopsis
    #  Calculate the size of a folder on disk
    #.Description
    #  Recursively calculate the size of a folder on disk,
    #  outputting it's size, and that of all it's children,
    #  and optionally, all of their children
    param(
        [string]$root=$PWD,
        # Show the size for each descendant recursively (otherwise, only immediate children)
        [Int]$Depth=1,
        [Switch]$Force
    )
    # Get the full canonical FileSystem path:
    $root = Convert-Path $root
    $DetailDepth = $Depth - 1
    $size = 0
    $files = 0
    $folders = 0
    $items = Get-ChildItem $root -Force:$Force
    foreach($item in $items) {
        if($item.PSIsContainer) {

            # Clear subItem in case the directory is empty.
            $subItem = @{ Folders = 0
                Files = 0
                Size = 0
                Name = $item.FullName }


            # Call myself recursively to calculate subfolder size
            # Since we're streaming output as we go, 
            #   we only use the last output of a recursive call
            #   because that's the summary object
            if($DetailDepth -ge 1) {
                Get-Size $item.FullName -Depth $DetailDepth -Force:$Force | Tee-Object -Variable subItems
                if($subItems.GetType().FullName -eq "System.Management.Automation.PSCustomObject") {
                    $subItem = $subItems
                } else {
                    $subItem = $subItems[-1]
                }
            } else {

                $subItem = Get-Size $item.FullName -Force:$Force | Select -Last 1
            }
            # The (recursive) size of all subfolders gets added
            $size += $subItem.Size
            $folders += $subItem.Folders + 1
            $files += $subItem.Files
            # avoid duplication by letting the parent report the summary values, except at the lowest detail depth
            if( $DetailDepth -lt 1) { 
                Write-Output $subItem
            }
        } else {
            $files += 1
            $size += $item.Length
        }
    }

    # in PS3, use the CustomObject trick to control the output order
    if($PSVersionTable.PSVersion -ge "3.0") {
        [PSCustomObject]@{ 
            Folders = $folders
            Files = $files
            Size = $size
            Name = $root
        }
    } else {
        New-Object PSObject -Property @{ 
            Folders = $folders
            Files = $files
            Size = $size
            Name = $root
        } 
    }
}
于 2014-12-19T19:29:32.763 回答
1

我是 Get-DiskUsage 脚本的创建者。老实说,我不知道为什么它不为您选择 -Path 。对我来说很好。真的很奇怪。快速提问 - 您是否尝试将路径输入为 D: 而不是 D:\?

@Jaykul - 非常好的替代方法。我必须承认,我不知道在 recusion 中使用递归对内存的影响。我总是将缓慢归因于 $results 在所有其他计算完成之前不会输出这一事实。我进行递归然后再次进行递归的原因很简单 - 第一次递归列出了根路径中的所有文件夹(将创建列表),第二个我注意到我需要,因为如果我不这样做,结果将是一个大小并不总是准确的

于 2013-05-23T04:55:36.397 回答