Как добавить вычисляемое свойство к выводу команды Powershell


11 августа 2022


Создание вычисляемых свойств в Powershell со ScriptProperty и Add-Member

При выполнении заранее созданных команд в Powershell мы получаем разные виды свойств. Эти свойства могут быть статическими, например, как путь до файла или вычисляемыми например как размер в мегабайтах. В этой статье будет рассмотрено как создать такое вычисляемое свойство.

 

 

Вывод свойства и их значений

Свойства и их значения можно вывести при простом выполнении команды, например:

Get-ChildItem 'C:\Windows\'

Чаще всего выводится ограниченное количество свойств. Все их можно вывести так:

Get-ChildItem 'C:\Windows\' | select *
# или
Get-ChildItem 'C:\Windows\' | select Length,Name
# или
(Get-ChildItem 'C:\Windows\').Name

Вывод свойств в Powershell

Посмотреть какие свойства есть у команды (объекта) так же можно через 'Get-Member':

Get-ChildItem 'C:\Windows\' | Get-Member -MemberType *Property*

Через 'Get-Member'  можно увидеть, является ли свойство статическим либо вычисляется в момент вызова команды.

Вывод свойств в Powershell с Get-Member

 

Добавление вычисляемых свойств через Add-Member

В предыдущих примерах возвращался список файлов, но размер файлов указывался в байтах. По умолчанию, преобразовать байты в Мегабайты можно так:

$size = 5126776
[math]::Round(($size / 1MB), 2)

Преобразования байт в биты в Powershell с округлением

Можно использовать этот подход добавив его как свойство к объекту используя 'Add-Member':

# получаем список файлов из директории
$files = Get-ChildItem 'C:\Windows\' -File
# создаем свойство, которое вычисляется из свойства Length 
$files | Add-Member -MemberType ScriptProperty `
  -Name 'Size' `
  -Value {[math]::Round(($this.Length / 1MB), 2)}
  
$files | select Name,Size | Sort Size -Descending

Добавление свойства с преобразованием битов в мегабайты Powershell

$this - это переменная, которая похожа на $PSItem (или $_). Она обозначает текущий объект, а Lenngth - это его свойство. Т.к. $files - это множество объектов, а не один, $this помогает обратиться к каждому из них.

Использование 'Add-Member' имеет минус в том, что его нужно применять к каждому результату (или переменной).

 

Обновление типов и Types.ps1xml

В директории $PSHOME находится файл 'Types,ps1xml'. Внутри этого файла находятся все дополнительные свойства и некоторые методы, которые есть у команд.

Директория работы Powershell

Свойства, перечисленные в этом файле, привязываются не к  самим командам, а к классам .NET. Какой класс использует команда можно увидеть так:

Get-ChildItem 'C:\Windows\' -File | Get-Member

Просмотр класса команды Powershell через Get-MemberЕсли открыть файл 'Types.ps1xml', то можно увидеть некоторые свойства со скриншота выше.

Формат Types.ps1xml в Powershell

Если вы хотите создать вычисляемое свойство, то у вас есть две возможности. Первая - это изменить сам файл 'Types.ps1xml' добавив в него нужный блок:

      <ScriptProperty>
        <Name>Size</Name>
        <GetScriptBlock>
            [math]::Round(($this.Length / 1MB), 2)
        </GetScriptBlock>
      </ScriptProperty>

Добавление типа в Types.ps1xml в Powershell

Либо вы можете создать отдельный файл, с расширением 'ps1xml', и объявить целый блок:

<Types>  
  <Type>
    <Name>System.IO.FileInfo</Name>
    <Members>
      <ScriptProperty>
        <Name>Size</Name>
        <GetScriptBlock>
            [math]::Round(($this.Length / 1MB), 2)
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>

Создание нового типа в Types.ps1xml в Powershell

Создавая отдельный 'ps1xml' файл и добавляя свойства, вы не заменяете оригинальный файл, а расширяете его.

Если этот файл будет находиться в директории $PSHOME и иметь расширение 'ps1xml', то он будет автоматически загружаться в каждой новой сессии Powershell. Если вы поместите его в другую директории, то можете загружать его через команду 'Update-TypeData' или использовать профиль Powershell.

Пример того, как можно отдельно импортировать файл и вывести созданное свойство:

Update-TypeData 'C:\size.ps1xml'
Get-ChildItem 'C:\Windows\' -File | Get-Member -MemberType ScriptProperty
Get-ChildItem 'C:\Windows\' -File | select Size

Просмотр вычисляемого свойства в Powershell

 

Пример на Get-Process

Команда 'Get-Process', по умолчанию, выводит информацию в байтах. В то же время 'Task Manager', делает это в процентах и мегабайтах. Мы можем создать аналогичные свойства и использовать их.

Вывод в мегабайтах

Перевести байты в мегабайты, округлив значения, можно так (на примере Working Set):

[math]::Round(($PSItem.WS / 1MB), 0)

В XML файл так же можно добавить 'PropertySet' (комплект свойств), благодаря которому не нужно будет указывать каждое из свойств отдельно:

<Types>
  <Type>
    <Name>System.Diagnostics.Process</Name>
    <Members>
      <PropertySet>
        <Name>MB</Name>
        <ReferencedProperties>
          <Name>Handles</Name>
          <Name>NPM(MB)</Name>
          <Name>PM(MB)</Name>
          <Name>WS(MB)</Name>
          <Name>CPU</Name>
          <Name>Id</Name>
          <Name>SI</Name>
          <Name>ProcessName</Name>
        </ReferencedProperties>
      </PropertySet>
      <ScriptProperty>
        <Name>NPM(MB)</Name>
        <GetScriptBlock>
            [math]::Round(($this.NPM / 1MB), 0)
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>PM(MB)</Name>
        <GetScriptBlock>
            [math]::Round(($this.PM / 1MB), 0)
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>WS(MB)</Name>
        <GetScriptBlock>
            [math]::Round(($this.WS / 1MB), 0)
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>

В примере выше идет расчет на то, что используется отдельный '.ps1xml' файл, в котором не было данных. Если это не так, то может потребоваться использовать только блок с <Type></Type>, без тегов <Types></Types>.

"Property Set" можно выбрать через команду 'Select':

Update-TypeData -Path "C:\file.types.ps1xml"
Get-Process | select MB | ft

Вывод процессов с Get-Process в мегабайтах с Powershell

Вывод в процентах

Главная проблема вычисляемых свойств в том, что процесс вычисления происходит для каждого значения отдельно. Вычисляя занимаемый процессом процент от оперативной памяти мы должны будем каждый раз вычислять общий объем RAM. Пример того, как это может быть:

# 1% от общего числа RAM
$ram_procent = ((Get-WMIObject Win32_PhysicalMemory).Capacity | Measure-Object -Sum).Sum / 100

Get-Process -Name 'Chrome' | Select-Object Name,id,@{Name='MemProcent';Expression={
            [math]::Round(($_.WorkingSet/$ram_procent), 2)
        }
    }

Вывод занятой оперативной памяти в процентах с Powershell

Процентная загруженность процессора вычисляется за счет времени, в течении которого он занят конкретным процессом. Что бы получить эти данные, для конкретного процесса, есть отдельный счетчик. Проблема в том, что он выполняется очень долго и на получение этого % для всего 'Get-Process' могут уйти минуты:

$proc = (Get-Process -name 'chrome' | select -Last 1).Name
(Get-Counter "\Процесс($proc)\% загруженности процессора" -ErrorAction SilentlyContinue).CounterSamples

Вывод занятости процессора в процентах с Powershell

XML файл может выглядеть так:

<Types>
  <Type>
    <Name>System.Diagnostics.Process</Name>
    <Members>
      <PropertySet>
        <Name>Procent</Name>
        <ReferencedProperties>
          <Name>Name</Name>
          <Name>Memory(%)</Name>
          <Name>Processor(%)</Name>
        </ReferencedProperties>
      </PropertySet>
      <ScriptProperty>
        <Name>Memory(%)</Name>
        <GetScriptBlock>
            $ram_procent = ((Get-WMIObject Win32_PhysicalMemory).Capacity | Measure-Object -Sum).Sum / 100
            [math]::Round(($this.WorkingSet/$ram_procent), 2)
        </GetScriptBlock>
      </ScriptProperty>
      <ScriptProperty>
        <Name>Processor(%)</Name>
        <GetScriptBlock>
             [math]::Round((Get-Counter "\Процесс($($this.Name))\% загруженности процессора" -ErrorAction SilentlyContinue).CounterSamples.CookedValue, 2)
        </GetScriptBlock>
      </ScriptProperty>
    </Members>
  </Type>
</Types>

Какой может получится результат:

Update-TypeData -Path "C:\file.types.ps1xml"
Get-Process | select Procent | ft

Вывод Get-Process в процентах Powershell

Счетчик, который возвращает процент занятости процессора, можно выполнить так, что он вернет сразу все результаты. Это займет столько же времени, сколько и на 1 процесс в примере выше.

(Get-Counter "\Процесс(*)\% загруженности процессора").CounterSamples

...

Теги: #powershell


Каналы
Telegram FixMyPc Telegram Лента FixMyPC RSS Rss
Популярные тэги
О блоге
Этот блог представляет собой конспекты выученного материала, приобретённого опыта и лучшие практики в системном администрировании и программировании.