我正在编写一个管理脚本,我需要计算磁盘上文件的大小。
这些文件位于压缩的NTFS卷上。
我无法使用FileInfo.Length
,因为这是文件大小而不是磁盘上的大小。例如,如果我有一个100MB的文件,但由于NTFS压缩它只使用25MB,我需要我的脚本返回25MB。
有没有办法在Powershell中执行此操作?
(我知道GetCompressedFileSize()
Win32调用,但我希望这已经在某种程度上受到了干扰。)
答案 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资源管理器显示,尤其是对于小文件。
描述了获取该信息的(几乎)正确方法答案 5 :(得分:0)
$ s =(compact / q C:\ whatever.dat | where-object {$ _。contains('total bytes')})。split()}; $ s [8] .padleft(20)+ $ S [0] .padleft(20)