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