Как получить фактический размер файла на диске из PowerShell? - PullRequest
8 голосов
/ 16 февраля 2009

Я пишу административный скрипт, и мне нужно рассчитать размер файлов на диске.

Эти файлы находятся на сжатом томе NTFS.

Я не могу использовать FileInfo.Length, потому что это размер файла, а не размер на диске. Например, если у меня есть файл размером 100 МБ, но он использует только 25 МБ из-за сжатия NTFS, мне нужен мой скрипт, чтобы вернуть 25 МБ.

Есть ли способ сделать это в Powershell?

(Я знаю о GetCompressedFileSize() вызове Win32, но я надеялся, что это уже завершено на каком-то уровне.)

Ответы [ 6 ]

9 голосов
/ 20 февраля 2009

(редактировать)

Я выяснил, как динамически добавлять свойство (называемое «свойством сценария») в Fileobject, поэтому теперь я могу использовать синтаксис: $ theFileObject.CompressedSize для чтения размера.

(конец редактирования)

Прочитайте ответ Goyuix, и я подумал: "Круто, но разве в Powershell нет какой-то возможности расширения типа?" И тогда я нашел этот пост Скотта Хансельмана: http://www.hanselman.com/blog/MakingJunctionsReparsePointsVisibleInPowerShell.aspx

И я создал свойство Script для объекта FileInfo: CompressedSize.

Вот что я сделал: (примечание: я совершенно новичок в Powershell, или, по крайней мере, я не очень часто его использую. Возможно, это можно сделать намного лучше, но вот что я сделал:

Сначала я скомпилировал Ntfs.ExtendedFileInfo из поста Goyuix. Я поместил 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 к объектам FileInfo, которые возвращаются get-childitem / dir. Но 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, вы можете получить доступ к свойству CompressedSize объекта FileInfo, как если бы это было любое другое свойство. Пример:

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

$ myFile.CompressedSize

Это работает (на моей машине, во всяком случае), но я хотел бы услышать, соответствует ли это лучшим практикам. Одна вещь, которую я знаю, я делаю неправильно: в файле Profile.ps1 я возвращаю результаты LoadFile в переменную, которую я не собираюсь использовать ($ null = blah blah). Я сделал это, чтобы подавить отображение результата загрузки файла на консоль. Вероятно, есть лучший способ сделать это.

8 голосов
/ 17 февраля 2009

Загрузите API управляемого Windows (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")
5 голосов
/ 21 февраля 2009

Легко сделать, используя 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")

Эксперимент! Наслаждайтесь! Engage!

Джеффри Сновер [MSFT] Архитектор Windows Management Partner Посетите блог группы разработчиков Windows PowerShell по адресу: http://blogs.msdn.com/PowerShell Посетите центр сценариев Windows PowerShell по адресу: http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

2 голосов
/ 18 февраля 2009

Если вы не можете найти понравившийся вам управляемый API, в PowerShell V2 намного проще P / Invoke Win32 API. Прочитайте инструкции PowerShell P / Invoke для получения инструкций.

1 голос
/ 20 июня 2014

Обратите внимание, что это не возвращает "размер на диске", который отображает Проводник Windows, особенно для небольших файлов.

(Почти) правильный способ получения этой информации описан в Получение «размера на диске» для небольших файлов в Powershell

0 голосов
/ 03 июля 2009

$ s = (compact / q C: \ what.dat | где-объект {$ _. Содержит ('всего байт')}). Split ()}; $ s [8] .padleft (20) + $ S [0] .padleft (20) * * тысяча один

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...