2009-02-16 20 views
8

Sto scrivendo uno script amministrativo e ho bisogno di calcolare la dimensione dei file sul disco.Come ottenere la dimensione effettiva su disco di un file da PowerShell?

Questi file si trovano su un volume NTFS compresso.

Non posso usare FileInfo.Length, perché questa è la dimensione del file e non dimensione su disco. Ad esempio, se ho un file da 100 MB, ma utilizza solo 25 MB a causa della compressione NTFS, ho bisogno che il mio script restituisca 25 MB.

C'è un modo per farlo in PowerShell?

(che so su Win32 chiamata GetCompressedFileSize(), ma speravo che questo è già wrappered a qualche livello.)

+0

Vuoi includere lo spazio su disco sprecato a causa di spazio inutilizzato grappolo? (questo è complicato con file di piccole dimensioni a causa dell'inlining nella MFT) o è solo l'aspetto compresso sufficiente – ShuggyCoUk

risposta

9

(edit)

ho capito come aggiungere dinamicamente una proprietà (chiamato "proprietà script") al FileObject, così ora, posso utilizzare la sintassi: $ theFileObject.CompressedSize di leggere il formato.

(fine edit)

risposta Leggi Goyuix di, e ho pensato "Cool, ma non c'è qualche tipo di capacità di tipo-extension in PowerShell?". Quindi ho trovato questo post di Scott Hanselman: http://www.hanselman.com/blog/MakingJunctionsReparsePointsVisibleInPowerShell.aspx

E ho creato una proprietà di script per l'oggetto FileInfo: CompressedSize.

Ecco quello che ho fatto: (nota: Sono abbastanza nuovo a PowerShell, o almeno io non lo uso molto questo potrebbe probabilmente essere fatto molto meglio, ma ecco quello che ho fatto:.

in primo luogo, ho compilato il Ntfs.ExtendedFileInfo dal post di Goyuix. ho messo la DLL nella cartella del mio profilo PowerShell (Documents \ WindowsPowerShell)

Successivamente, ho creato un file nel mio profilo directory denominata My.Types.ps1xml.

Inserisco il seguente codice XML nel file:

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

Tale codice (una volta unito al sistema di tipi) aggiungerà dinamicamente una proprietà denominata CompressedSize agli oggetti FileInfo restituiti da get-Childitem/dir.Ma Powershell non è ancora al corrente del codice, e non conosce ancora la mia DLL. Lo gestiamo nel passaggio successivo:

Modifica profilo.ps1. nella stessa directory. Ora, il mio file Profile ha già alcune cose perché ho le estensioni della Community per powershell installate. Spero di aver incluso tutto ciò che ti serve in questo prossimo frammento di codice, in modo che funzioni anche su una macchina che non ha le estensioni. Aggiungere il seguente codice a 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 

Ora, la variabile $ ProfileDir che ho di riferimento è definito in precedenza nel mio script Profile.ps1. Solo nel caso che non è nel tuo, qui è la definizione:

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

Questo è tutto. Alla successiva esecuzione di Powershell, è possibile accedere alla proprietà CompressedSize sull'oggetto FileInfo come se fosse un'altra proprietà. Esempio:

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

$ myFile.CompressedSize

Questo funziona (sulla mia macchina, comunque), ma mi piacerebbe sentire se va bene con le migliori pratiche. Una cosa so che sto sbagliando: nel file Profile.ps1, restituisco i risultati di LoadFile in una variabile che non userò ($ null = blah blah). L'ho fatto per sopprimere la visualizzazione del risultato del file di caricamento sulla console. C'è probabilmente un modo migliore per farlo.

8

Caricare fino API di Windows Managed (http://mwinapi.sourceforge.net/) e controllare la classe ExtendedFileInfo. C'è un metodo GetPhysicalFileSize() che restituirà la dimensione richiesta da un file su disco.

public static ulong GetPhysicalFileSize(string filename) 

Aternatively, si potrebbe compilare il proprio DLL e caricare l'assembly per che una funzione:

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

Quindi per compilare:

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

E, infine, per caricare ed eseguire in PowerShell:

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

Se non riesci a trovare l'API gestita che ti piace, in PowerShell V2 è molto più semplice P/Richiamare un'API Win32. Leggi PowerShell P/Invoke Walkthrough per le istruzioni.

5

facile da fare utilizzando V2 Add-Type e 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") 

esperimento! Godere! Engage!

Jeffrey Snover [MSFT] Windows Management Partner Architetto visitare il blog di Windows PowerShell della squadra a: http://blogs.msdn.com/PowerShell Visita di Windows PowerShell ScriptCenter a: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

+0

Adoro la semplicità di questa soluzione ... ma per me restituisce sempre lo stesso valore della "lunghezza", anche quando dovrebbe essere diverso. È una limitazione della chiamata API Win32? – ewall

+1

@wall Questo è un vecchio post ma l'API corretta da chiamare è qui: http://stackoverflow.com/a/22508299/520612 –

0

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