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

.net 如何从PowerShell获取文件在磁盘上的实际大小?,.net,powershell,.net,Powershell,我正在编写一个管理脚本,需要计算磁盘上文件的大小 这些文件位于压缩的NTFS卷上 我不能使用FileInfo.Length,因为这是文件大小,而不是磁盘上的大小。例如,如果我有一个100MB的文件,但由于NTFS压缩,它只使用了25MB,那么我需要脚本返回25MB 在Powershell中有这样做的方法吗 (我知道GetCompressedFileSize()Win32调用,但我希望它已经在某种程度上包装好了。)加载托管Windows API()并查看ExtendedFileInfo类。有一种方

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

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

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

在Powershell中有这样做的方法吗


(我知道
GetCompressedFileSize()
Win32调用,但我希望它已经在某种程度上包装好了。)

加载托管Windows API()并查看ExtendedFileInfo类。有一种方法可以返回文件在磁盘上所需的大小

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;
      }
    }
  }
}
最后,要在PowerShell中加载和运行:

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

如果您找不到自己喜欢的托管API,那么在PowerShell V2中,p/调用Win32 API要容易得多。阅读说明。

(编辑)

我知道了如何将属性(称为“脚本属性”)动态添加到Fileobject,因此现在,我可以使用语法:$theFileObject.CompressedSize来读取大小

(编辑结束)

阅读Goyuix的回答,我想“很酷,但是Powershell中没有某种类型的扩展功能吗?”。于是我找到了斯科特·汉斯曼的帖子:

我为FileInfo对象创建了一个脚本属性: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>
现在,我引用的$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)。我这样做是为了禁止将加载文件的结果显示到控制台。可能有更好的方法。

使用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")
添加类型-type@'
使用制度;
使用System.Runtime.InteropServices;
使用系统组件模型;
名称空间win32函数
{
公共类ExtendedFileInfo
{
[DllImport(“kernel32.dll”,SetLastError=true,EntryPoint=“GetCompressedFileSize”)]
静态外部单元GetCompressedFileSizeAPI(字符串lpFileName,输出单元lpFileSizeHigh);
公共静态ulong GetCompressedFileSize(字符串文件名)
{
单位高;
单位低;
低=GetCompressedFileSizeAPI(文件名,输出高);
int error=Marshal.GetLastWin32Error();
if(高==0&&low==0xFFFFFFFF&&error!=0)
抛出新的Win32Exception(错误);
其他的

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

请注意,这不会返回Windows资源管理器显示的“磁盘大小”,尤其是对于小文件


获取该信息的(几乎)正确方法如

所述。是否要包括由于未使用的群集空间而浪费的磁盘空间?(由于MFT中的内联,这对于小文件很棘手)或者仅仅是压缩方面就足够了我喜欢这个解决方案的简单性…但对我来说,它总是返回与“长度”相同的值,即使它应该是不同的。这是Win32 API调用的限制吗?@E这是一篇旧文章,但正确的调用API在这里:如果你想让它与所有的API兼容(Unicode编码)文件名,您需要使用GetCompressedFileSizeView:[DllImport(“kernel32.dll”,SetLastError=true,EntryPoint=“GetCompressedFileSizeW”)]静态外部uint GetCompressedFileSizeAPI([Marshallas(UnmanagedType.LPWStr)]字符串lpFileName,out uint lpFileSizeHigh;
$ProfileDir = split-path $MyInvocation.MyCommand.Path -Parent
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")