2009-02-16 19 views
8

Estoy escribiendo un script administrativo, y necesito calcular el tamaño de los archivos en el disco.¿Cómo obtener el tamaño real en disco de un archivo de PowerShell?

Estos archivos están en un volumen NTFS comprimido.

No puedo usar FileInfo.Length, porque ese es el tamaño del archivo y no el tamaño en el disco. Por ejemplo, si tengo un archivo de 100MB, pero solo usa 25MB debido a la compresión NTFS, necesito que mi script devuelva 25MB.

¿Hay alguna manera de hacer esto en Powershell?

(que sé acerca de la llamada Win32 GetCompressedFileSize(), pero esperaba que esto ya está wrappered en algún nivel.)

+0

¿Quieres incluir el espacio en disco desperdiciado por el espacio no utilizado del clúster? (Esto es complicado con archivos pequeños debido a la inclusión en el MFT) o solo es el aspecto comprimido suficiente – ShuggyCoUk

Respuesta

9

(editar)

me di cuenta de cómo agregar dinámicamente una propiedad (llamada "propiedad de script") a la FileObject, por lo que ahora, puedo usar la sintaxis: $ theFileObject.CompressedSize para leer el tamaño.

(final de la edición)

respuesta de Lee Goyuix, y pensé "Cool, pero no está allí algún tipo de capacidad de tipo de extensión en Powershell?". Entonces encontré esta publicación de Scott Hanselman: http://www.hanselman.com/blog/MakingJunctionsReparsePointsVisibleInPowerShell.aspx

Y creé una propiedad de script para el objeto FileInfo: CompressedSize.

Aquí es lo que hice: (nota: Soy bastante nuevo en Powershell, o al menos yo no lo uso mucho Esto probablemente se podría hacer mucho mejor, pero esto es lo que hice:.

en primer lugar, he recopilado la Ntfs.ExtendedFileInfo desde el puesto de Goyuix. pongo la DLL en mi directorio del perfil Powershell (documentos \ WindowsPowerShell)

a continuación, he creado un archivo en mi directorio de perfil denominado My.Types.ps1xml.

Puse el siguiente XML en el archivo:

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

Ese código (una vez fusionado en el sistema de tipos) agregará dinámicamente una propiedad denominada CompressedSize a los objetos FileInfo que devuelve get-childitem/dir.Pero Powershell aún no conoce el código, y aún no sabe sobre mi DLL. Nos ocupamos de eso en el próximo paso:

Editar Profile.ps1. en el mismo directorio. Ahora, mi archivo de perfil ya tiene algunas cosas porque tengo las extensiones de comunidad para powershell instaladas. Afortunadamente, incluyo todo lo que necesita en este próximo fragmento de código, para que funcione incluso en una máquina que no tenga las extensiones. Agregue el código siguiente para 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 

Ahora, la variable $ ProfileDir que referencia se define anteriormente en mi guión Profile.ps1. En caso de que no esté en el tuyo, aquí está la definición:

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

Eso es todo. La próxima vez que ejecute Powershell, puede acceder a la propiedad ComprimirTamaño en el objeto FileInfo como si se tratara de otra propiedad. Ejemplo:

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

$ myFile.CompressedSize

Esto funciona (en mi máquina, de todos modos), pero me encantaría saber si se ajusta con las mejores prácticas. Una cosa que sé que estoy haciendo mal: en el archivo Profile.ps1, devuelvo los resultados de LoadFile a una variable que no voy a usar ($ null = blah blah). Lo hice para suprimir la visualización del resultado del archivo de carga en la consola. Probablemente haya una mejor manera de hacerlo.

8

de carga hasta la API de Windows Gestionado (http://mwinapi.sourceforge.net/) y echa un vistazo a la clase ExtendedFileInfo. Existe un método GetPhysicalFileSize() que devolverá el tamaño que un archivo requiere en el disco.

public static ulong GetPhysicalFileSize(string filename) 

Aternatively, usted podría compilar su propia DLL y cargar el ensamblado para que una función:

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; 
     } 
    } 
    } 
} 

continuación para compilar:

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

Y, por último, para cargar y ejecutar en PowerShell:

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

Si no puede encontrar la API administrada que le gusta, en PowerShell V2 es mucho más fácil P/Invocar una API de Win32. Lee PowerShell P/Invoke Walkthrough para obtener instrucciones.

5

Fácil de hacer uso de V2 Add-Type y 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") 

Experimento! ¡Disfrutar! ¡Contratar!

Jeffrey Snover [MSFT] socio de administración de Windows Arquitecto Visita el blog del equipo de Windows PowerShell en: http://blogs.msdn.com/PowerShell Visita la PowerShell de Windows en ScriptCenter: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

+0

Me encanta la simplicidad de esta solución ... pero para mí siempre devuelve el mismo valor que la "longitud", incluso cuando debería ser diferente ¿Es eso una limitación de la llamada API de Win32? – ewall

+1

@ewall Esta es una publicación anterior pero la API correcta para llamar está aquí: http://stackoverflow.com/a/22508299/520612 –

0

$ s = (compacto/q C: \ whatever.dat | donde-objeto {$ _. contiene ('bytes totales')}). split()}; $ s [8] .padleft (20) + $ s [0] .padleft (20)

Cuestiones relacionadas