如何从PowerShell获取文件的实际磁盘大小?

时间:2009-02-16 18:03:02

标签: .net powershell

我正在编写一个管理脚本,我需要计算磁盘上文件的大小。

这些文件位于压缩的NTFS卷上。

我无法使用FileInfo.Length,因为这是文件大小而不是磁盘上的大小。例如,如果我有一个100MB的文件,但由于NTFS压缩它只使用25MB,我需要我的脚本返回25MB。

有没有办法在Powershell中执行此操作?

(我知道GetCompressedFileSize() Win32调用,但我希望这已经在某种程度上受到了干扰。)

6 个答案:

答案 0 :(得分:9)

(编辑)

我想出了如何动态地向Fileobject添加属性(称为“脚本属性”),所以现在,我可以使用以下语法:$ theFileObject.CompressedSize来读取大小。

(编辑结束)

阅读Goyuix的回复,我认为“很酷,但在Powershell中不存在某种类型扩展功能吗?”。所以我发现这个Scott Hanselman帖子:http://www.hanselman.com/blog/MakingJunctionsReparsePointsVisibleInPowerShell.aspx

我为FileInfo对象创建了一个Script属性:CompressedSize。

这就是我所做的:(注意:我对Powershell很新,或者至少我没有太多使用它。这可能会变得更好,但这就是我所做的:

首先,我从Goyuix的帖子中编译了Ntfs.ExtendedFileInfo。我把DLL放在我的Powershell配置文件目录(Documents \ WindowsPowershell)

接下来,我在我的个人资料目录中创建了一个名为My.Types.ps1xml的文件。

我将以下XML放入文件中:

<Types>
<Type>
    <Name>System.IO.FileInfo</Name>
    <Members>
        <ScriptProperty>
          <Name>CompressedSize</Name>
          <GetScriptBlock>
            [Ntfs.ExtendedFileInfo]::GetCompressedFileSize($this.FullName)
          </GetScriptBlock>
        </ScriptProperty>
    </Members>
</Type>
</Types>

该代码(一旦合并到类型系统中)将动态地将名为CompressedSize的属性添加到get-childitem / dir返回的FileInfo对象中。但Powershell还不知道代码,它还不知道我的DLL。我们将在下一步处理这个问题:

编辑Profile.ps1。在同一目录中。现在,我的配置文件恰好已经包含了一些内容,因为我安装了PowerShell社区扩展。希望我在下一个代码片段中包含您需要的所有内容,以便它甚至可以在没有扩展的机器上运行。将以下代码添加到Profile.ps1:

#This will load the ExtendedfileInfo assembly to enable the GetCompressedFileSize method.  this method is used by the
#PSCompressedSize Script Property attached to the FileInfo object.
$null = [System.Reflection.Assembly]::LoadFile("$ProfileDir\ntfs.extendedfileinfo.dll") 

#merge in my extended types
$profileTypes = $ProfileDir | join-path -childpath "My.Types.ps1xml"
Update-TypeData $profileTypes

现在,我引用的$ ProfileDir变量先前在我的Profile.ps1脚本中定义。万一它不在你的身上,这就是定义:

$ProfileDir = split-path $MyInvocation.MyCommand.Path -Parent

就是这样。下次运行Powershell时,您可以访问FileInfo对象上的CompressedSize属性,就像它是任何其他属性一样。 例如:

$ myFile = dir c:\ temp \ myfile.txt

$ myFile.CompressedSize

这可行(在我的机器上,无论如何),但我很想知道它是否符合最佳实践。有一件事我知道我做错了:在Profile.ps1文件中,我将LoadFile的结果返回到一个我不打算使用的变量($ null = blah blah)。我这样做是为了禁止将加载文件的结果显示到控制台。可能有更好的方法。

答案 1 :(得分:8)

加载托管Windows API( http://mwinapi.sourceforge.net/)并查看ExtendedFileInfo类。有一种方法GetPhysicalFileSize()将返回文件在磁盘上所需的大小。

public static ulong GetPhysicalFileSize(string filename)

或者,您可以编译自己的DLL并加载该函数的程序集:

using System;
using System.Runtime.InteropServices;

namespace NTFS {
  public class ExtendedFileInfo
  {
    [DllImport("kernel32.dll", SetLastError=true, EntryPoint="GetCompressedFileSize")]
    static extern uint GetCompressedFileSizeAPI(string lpFileName, out uint lpFileSizeHigh);
    public static ulong GetCompressedFileSize(string filename)
    {
      uint high;
      uint low;
      low = GetCompressedFileSizeAPI(filename, out high);
      int error = Marshal.GetLastWin32Error();
      if (high == 0 && low == 0xFFFFFFFF && error != 0)
      {
        throw new System.ComponentModel.Win32Exception(error);
      }
      else
      {
        return ((ulong)high << 32) + low;
      }
    }
  }
}

然后编译:

csc /target:library /out:ntfs.extendedfileinfo.dll ntfs.extendedfileinfo.cs 

最后,在PowerShell中加载并运行:

PS C:\> [System.Reflection.Assembly]::LoadFile("C:\ntfs.extendedfileinfo.dll")
PS C:\> [NTFS.ExtendedFileInfo]::GetCompressedFileSize("C:\sample.txt")

答案 2 :(得分:5)

使用V2 Add-Type和Pinvoke.NET很容易:

add-type -type  @'
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace Win32Functions
{
    public class ExtendedFileInfo
    {
        [DllImport("kernel32.dll", SetLastError=true, EntryPoint="GetCompressedFileSize")]
        static extern uint GetCompressedFileSizeAPI(string lpFileName, out uint lpFileSizeHigh);

        public static ulong GetCompressedFileSize(string filename)
        {
            uint high;
            uint low;
            low = GetCompressedFileSizeAPI(filename, out high);
            int error = Marshal.GetLastWin32Error();
            if (high == 0 && low == 0xFFFFFFFF && error != 0)
            throw new Win32Exception(error);
            else
            return ((ulong)high << 32) + low;
        }
    }
}
'@

[Win32Functions.ExtendedFileInfo]::GetCompressedFileSize( "C:\autoexec.bat")

实验!请享用!接合!

Jeffrey Snover [MSFT] Windows Management Partner Architect 访问Windows PowerShell团队博客:http://blogs.msdn.com/PowerShell 访问Windows PowerShell ScriptCenter:http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

答案 3 :(得分:2)

如果找不到您喜欢的托管API,那么在PowerShell V2中,P / Invoke Win32 API要容易得多。请阅读PowerShell P/Invoke Walkthrough以获取相关说明。

答案 4 :(得分:1)

请注意,这并不会在磁盘上返回&#34;大小&#34; Windows资源管理器显示,尤其是对于小文件。

Getting "size on disk" for small files in Powershell

描述了获取该信息的(几乎)正确方法

答案 5 :(得分:0)

$ s =(compact / q C:\ whatever.dat | where-object {$ _。contains('total bytes')})。split()}; $ s [8] .padleft(20)+ $ S [0] .padleft(20)