欢迎光临 Enjoy IT (ITECN.NET) 登录 | 注册 | 帮助

Windows PowerShell每周提示(35):确定文件夹大小

通常来说,Windows PowerShell使得你的系统管理生活变得非常容易。这很不错,除了一件事:当一些事发生错误时,人们会认为是他们自己的问题。

例如,在过去的几周中,我们收到了一些类似以下内容的电子邮件:“你好,脚本专家。我在一个你们可以想象的最简单的PowerShell脚本上花了异乎寻常的时间。我想做的是确定文件夹的大小,但是我却不能做到。无论我怎么尝试却总是得不到文件夹的大小。我做错什么了么?”

事实证明,你没有做错任何事。(除了向脚本专家求助这件事。)很有可能你使用了一个类似以下的命令,该命令绑定到C:\Script文件夹然后返回一些基本信息,包括文件夹大小(Length):

Get-Item C:\Scripts

下面是一些你可能得到的信息:

Directory: Microsoft.PowerShell.Core\FileSystem::C:\

 

 

Mode                LastWriteTime     Length Name

----                   -------------             ------  ----

d----             4/1/2008  12:39 PM          scripts

接着你就有了文件夹的大小……呃,什么都没有。我们肯定做错什么事了!听着,放松点,不管你做了什么,不要惊慌。像我们所说的,这不是你的错,这实际上是PowerShell的错。因为某些理由PowerShell无法直接返回文件夹的大小。(但是它至少有志同道合的伙伴,WMI也无法返回文件夹的大小。)

那么这是否意味着当遇到确定文件夹大小这个任务时你就运气不佳了呢?无论相信与否,回答是否定的,你没有遇上倒霉事。也许你无法直接确定文件夹的大小,但是你能间接确定文件夹的大小,通过使用类似以下的代码:

Get-ChildItem C:\Scripts | Measure-Object -property length -sum

姑且承认,这看上去是一个有点复杂的方法来确定文件夹大小,但是至少它的确确定了文件夹的大小。我们使用Get-ChildItem cmdlet来返回在文件夹C:\Scripts内找到的所有项目的集合。然后我们通过管道将集合传递给Measure-Object cmdlet并让Measure-Object完成两件事:

1.      查找集合中每一项的Length属性。

2.      报告每一项的Length属性所累加起来的和(sum

这会给我们带来什么?它将给我们带来类似以下的输出信息:

Count    : 58

Average  :

Sum      : 1244611

Maximum  :

Minimum  :

Property : length

那么文件夹C:\Scripts的大小是?根据Measure-Object提供的信息,大小为1,244,611字节。(Sum属性会告诉你答案。)

这不坏,像我们所说的,我们确实得到了我们正在寻找的答案。在另一方面,我们无法通过该方法,一眼看到结果是什么。这不仅是因为输出有点凌乱而且也和结果是以字节而不是兆字节方式来呈现有关。因此,让我们试试增强功能的代码块:

$colItems = (Get-ChildItem C:\Scripts | Measure-Object -property length -sum)

"{0:N2}" -f ($colItems.sum / 1MB) + " MB"

好的,这两行代码看上去也很复杂,不是么?也许我们要解释下这里代码的作用。在第一行我们做了和刚才一样的事:使用Get-ChildItem来返回文件夹C:\Scripts内所有对象的集合,然后让Measure-Object来计算所有对象大小累加起来的和。然而,这一次我们没有立即回报结果,取而代之的是,我们将输出结果保存在名为$colItems的变量中。

在第二行,我们对输出做了一点格式化。一开始,我们使用以下结构来取出Sum属性的值并将他转换为兆字节:

($colItems.sum / 1MB)

 

 

注意. 你说你无法理解该行代码如何将字节转换为千字节?那么你就该看看我们的相关提示:Byte Conversion.

 

 

 

一旦完成了转换,我们就使用下面的代码来显示带有两位小数的值:

"{0:N2}" -f

是的,事实上我们确实有关于格式化数字的一个提示来解释该代码是如何执行的。谢谢提问。

最终,我们把MB附加到输出的末端。最终结果?看上去像这样:

1.19 MB

好多了。

如你所见,这运行起来很棒。除了一件事:如果我们打卡Windows资源管理器并检查C:\Scripts文件夹的大小的话,Windows资源管理器指出文件夹的实际大小为3.60兆字节:

呀!我们又做错什么了么?

可能吧。但是不涉及使用Windows PowerShell来计算文件夹的大小。当你在Windows资源管理器中查看文件夹的大小时,Windows资源管理器告诉你这个文件夹的总大小,这其中不仅包括父文件夹(C:\Scripts),也包括该文件夹下所有子文件夹中的文件。如果我们把父文件夹中的文件大小加起来的话(也正是刚才脚本所做的)那么我们最后会发现文件夹大小为1.19兆字节。如果我们把C:\Scripts文件夹中的所有文件及其子文件中的所有文件加起来的话,最后会得到该文件夹的大小为3.60兆字节。

当然,我们刚才向你展示的脚本没有把C:\Scripts文件夹中所有子文件夹内的文件大小加起来。如果我们想要整个文件夹大小的话那么这就是一个问题。如果我们确实要这么做的话,那么就需要修改一下脚本。

什么意思?“一下”是多少?好的,事实证明,并不多。我们所要做的是向Get-ChildItem添加-recurse参数;该参数告诉Get-ChildItem返回C:\Scripts文件夹及其子文件夹中的所有文件。下面是我们修改过的脚本:

$colItems = (Get-ChildItem C:\Scripts -recurse | Measure-Object -property length -sum)

"{0:N2}" -f ($colItems.sum / 1MB) + " MB"

而以下内容则是我们运行脚本所得到结果:

3.60 MB

酷吧?

让我们再尝试点新奇的事。假设你喜欢一份完整的关于C:\Scripts文件夹及其个子文件夹大小的列表,一份类似以下内容的报表:

C:\Scripts -- 1.19 MB

C:\Scripts\BCD -- 0.02 MB

C:\Scripts\Forms -- 0.08 MB

C:\Scripts\Games -- 0.04 MB

C:\Scripts\Games\New Folder -- 0.02 MB

C:\Scripts\New Folder -- 2.23 MB

C:\Scripts\Test Folder -- 0.02 MB

我们如何产生一份类似的报表呢?下面是一种方法:

$startFolder = "C:\Scripts"

 

 

$colItems = (Get-ChildItem $startFolder | Measure-Object -property length -sum)

"$startFolder -- " + "{0:N2}" -f ($colItems.sum / 1MB) + " MB"

 

 

$colItems = (Get-ChildItem $startFolder -recurse | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object)

foreach ($i in $colItems)

    {

        $subFolderItems = (Get-ChildItem $i.FullName | Measure-Object -property length -sum)

        $i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"

    }

我们实际上在这里采取了两个步骤。一开始,我们使用以下代码块来决定C:\Scripts文件夹的大小,其中不包含任何子文件夹:

$startFolder = "C:\Scripts"

 

 

$colItems = (Get-ChildItem $startFolder | Measure-Object -property length -sum)

"$startFolder -- " + "{0:N2}" -f ($colItems.sum / 1MB) + " MB"

这部分的结果我们已经看到了。接下来,我们使用这行代码来返回在C:\Scripts中所有子文件夹的集合(也包括这些子文件夹里的文件夹),并按文件夹路径排序:

$colItems = (Get-ChildItem $startFolder -recurse | Where-Object {$_.PSIsContainer -eq $True} | Sort-Object)

如你所见。我们再次使用Get-ChildItem(及–recurse参数)来返回C:\Scripts中所有对象的集合。然而,一旦我们有了相关集合我们就立即将它传递给Where-Object cmdlet。然后我们让Where-Object把集合限制到PSIsContainer属性为真($True)的对象。这属性有何用?让我们来告诉你:如果PSIsContainer属性为真那意味着我们处理的是文件夹而不是文件。最终效果就是我们从集合中剔除了所有文件,仅留下在C:\Scripts中找到的所有子文件夹。

然后,为了让结果变得漂亮和整洁,我们将这个经过过滤的集合传递给Sort-Object,该cmdlet将按照文件夹路径对文件夹进行排序。

剩下的就简单了。首先,我们建立了foreach循环来遍历集合中的所有子文件夹:

foreach ($i in $colItems)

foreach循环内部我们使用两行代码来连接到子文件夹(可以参考FullName属性,该属性和文件夹路径是一样的)并计算它的大小:

$subFolderItems = (Get-ChildItem $i.FullName | Measure-Object -property length -sum)

$i.FullName + " -- " + "{0:N2}" -f ($subFolderItems.sum / 1MB) + " MB"

这件事就是这样来处理。

顺带说一句,还有一种方法来决定一个文件夹的总大小。这方法有点作弊的味道,并且也不是“纯正”的PowerShell解决方法,但是下面的脚本将会返回文件夹C:\Scripts(包括所有子文件夹)的大小:

$objFSO = New-Object -com  Scripting.FileSystemObject

"{0:N2}" -f (($objFSO.GetFolder("C:\Scripts").Size) / 1MB) + " MB"

我们在这里所做的事是使用我们的老朋友Scripting.FileSystemObject来确定文件夹的大小。在第一行我们使用New-Object cmdlet(并带上-com参数,因为我们创建的是COM对象)来创建FileSytemObject的新实例。在第二行我们使用这个语法(及GetFolder方法)来绑定到C:\Scripts文件夹并只返回Size属性的值:

 

 

 

($objFSO.GetFolder("C:\Scripts").Size)

随后我们做了一点美化,将大小转换为兆字节,格式化输出使它仅显示两位小数,并在输出最后附加上MB。最终结果应当看上去有点像这样:

3.60 MB

无可否认的是,FileSystemObject是陈旧的(据说)已经过时的技术,也几乎不是世界上最富有魅力的事物。但另一方面,它确实完成了这项任务。

通盘考虑的话,它也像脚本专家一样使人敬畏。

好的,除了一件事:像我们所说的,FileSytemObject确实完成了这项任务。

下周见。

英文原文

http://www.microsoft.com/technet/scriptcenter/resources/pstips/apr08/pstip0404.mspx

已发表 2008年5月5日 8:24 作者 ghjconan

评论

禁止匿名发表评论