При выполнении заранее созданных команд в Powershell мы получаем разные виды свойств. Эти свойства могут быть статическими, например, как путь до файла или вычисляемыми например как размер в мегабайтах. В этой статье будет рассмотрено как создать такое вычисляемое свойство.
Вывод свойства и их значений
Свойства и их значения можно вывести при простом выполнении команды, например:
Get-ChildItem 'C:\Windows\'
Чаще всего выводится ограниченное количество свойств. Все их можно вывести так:
Get-ChildItem 'C:\Windows\' | select *
# или
Get-ChildItem 'C:\Windows\' | select Length,Name
# или
(Get-ChildItem 'C:\Windows\').Name
Посмотреть какие свойства есть у команды (объекта) так же можно через 'Get-Member':
Get-ChildItem 'C:\Windows\' | Get-Member -MemberType *Property*
Через 'Get-Member' можно увидеть, является ли свойство статическим либо вычисляется в момент вызова команды.
Добавление вычисляемых свойств через Add-Member
В предыдущих примерах возвращался список файлов, но размер файлов указывался в байтах. По умолчанию, преобразовать байты в Мегабайты можно так:
$size = 5126776
[math]::Round(($size / 1MB), 2)
Можно использовать этот подход добавив его как свойство к объекту используя '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
$this - это переменная, которая похожа на $PSItem (или $_). Она обозначает текущий объект, а Lenngth - это его свойство. Т.к. $files - это множество объектов, а не один, $this помогает обратиться к каждому из них.
Использование 'Add-Member' имеет минус в том, что его нужно применять к каждому результату (или переменной).
Обновление типов и Types.ps1xml
В директории $PSHOME находится файл 'Types,ps1xml'. Внутри этого файла находятся все дополнительные свойства и некоторые методы, которые есть у команд.
Свойства, перечисленные в этом файле, привязываются не к самим командам, а к классам .NET. Какой класс использует команда можно увидеть так:
Get-ChildItem 'C:\Windows\' -File | Get-Member
Если открыть файл 'Types.ps1xml', то можно увидеть некоторые свойства со скриншота выше.
Если вы хотите создать вычисляемое свойство, то у вас есть две возможности. Первая - это изменить сам файл 'Types.ps1xml' добавив в него нужный блок:
<ScriptProperty>
<Name>Size</Name>
<GetScriptBlock>
[math]::Round(($this.Length / 1MB), 2)
</GetScriptBlock>
</ScriptProperty>
Либо вы можете создать отдельный файл, с расширением '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>
Создавая отдельный '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
Пример на 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
Вывод в процентах
Главная проблема вычисляемых свойств в том, что процесс вычисления происходит для каждого значения отдельно. Вычисляя занимаемый процессом процент от оперативной памяти мы должны будем каждый раз вычислять общий объем 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)
}
}
Процентная загруженность процессора вычисляется за счет времени, в течении которого он занят конкретным процессом. Что бы получить эти данные, для конкретного процесса, есть отдельный счетчик. Проблема в том, что он выполняется очень долго и на получение этого % для всего 'Get-Process' могут уйти минуты:
$proc = (Get-Process -name 'chrome' | select -Last 1).Name
(Get-Counter "\Процесс($proc)\% загруженности процессора" -ErrorAction SilentlyContinue).CounterSamples
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
Счетчик, который возвращает процент занятости процессора, можно выполнить так, что он вернет сразу все результаты. Это займет столько же времени, сколько и на 1 процесс в примере выше.
(Get-Counter "\Процесс(*)\% загруженности процессора").CounterSamples
...
Подписывайтесь на наш Telegram канал
Теги: #powershell