PowerShell Get-DiskUsage CmdLet:如何从不同的驱动器/目录列出?

时间:2013-04-23 20:49:43

标签: powershell powershell-v3.0

我是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

3 个答案:

答案 0 :(得分:3)

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对象,然后它再做一个递归目录扫描ON EACH FOLDER,创建一个新对象并对属性求和......它的速度非常慢,与du.exe相比会占用大量内存和IO

稍微好一点的方法

当你想要树中每个文件夹的SIZE时,递归地,以算法正确的方式执行该操作并不涉及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会更快,但它是一个单一的小马,有人必须专门为一个目的写。它仍然是最好的方法,如果你想做的就是运行它并读取输出。但如果你想在脚本中使用它的输出,它会生成你必须解析的文本输出,并且很难将这些信息收集作为整个仪表板式监视脚本输出的一部分...

答案 1 :(得分:1)

我是Get-DiskUsage脚本的创建者。我老实说不知道为什么它不会为你带走-Path。对我来说很好。真的很奇怪。但是快速问题 - 您是否尝试将路径键入D:而不是D:\?

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

答案 2 :(得分:1)

我喜欢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
        } 
    }
}