Примечание: данный пост перепечатан в связи с закрытием бложиков на spaces.live.com, как имеющий какую-то ценность для автора и/или читателей.
Ричард Сиддэвей (Richard Siddaway) в своём блоге ведёт на мой взгляд интересный цикл постов Windows 2000 Scripting Guide (W2KSG) с применением PowerShell и WMI. В них рассказываются достаточно интересные и полезные возможности классов WMI для сбора различных сведений как программной части системы, так и аппаратной. Так же недавно на форуме TechNet была поднята (да, на форумах всегда найдутся археологи, которые выкопают темы полу- и годичной давности, а то и ещё старше :) ) тема про скрипт, который бы собрал данные об аппаратной составляющей компьютеров. Подобные темы периодически всплывают на различных форумах. Я подумал, что неплохо было бы решить данный вопрос с помощью PowerShell.
Итак, отправной точкой для меня послужила ссылка на Computer System Hardware Classes, где я посмотрел какие классы можно применить. Изучив весь список я отобрал лишь самые необходимые для решения задачи классы, а именно:
Достаточно сходить по ссылкам и можно посмотреть множество свойств каждого класса, которые детально описывают себя. Но при прочтении очень важно следить за поддерживаемыми свойствами в ОС, которые были выпущены до Windows Vista/2008. Я старался эти моменты учитывать, чтобы получить оптимальную совместимость как с предыдущими ОС, так и с текущими.
Ничего сверхсложного в этом нету, сперва я определил требуемые классы WMI в переменные и определил набор необходимых свойств каждого класса следующим образом:
$OS = gwmi Win32_OperatingSystem | Select Caption, OSArchitecture, OtherTypeDescription, ServicePackMajorVersion, CSName, TotalVisibleMemorySize $CPU = gwmi Win32_Processor | Select Architecture, DeviceID, Name $RAM = gwmi Win32_MemoryDevice | Select DeviceID, StartingAddress, EndingAddress ...
После определения всех классов WMI и переменных я начал писать секцию вывода. Например, вывод имени компьютера и ОС, под которой он управляется:
"Computer Name: `n`t" + $OS.CSName + "`n" "Operating System: `n`t" + $OS.Caption + " " + $OS.OtherTypeDescription + $OS.OSArchitecture + "`n"
Здесь можно не обращать на знаки регулярных выражений, т.к. они несут только одну функцию, а именно - удобное для воспроиятия форматирование вывода. Т.е. первой строкой пойдёт название поля Computer Name, после чего будет переход на новую строку (`n). Чтобы все строчки не сливались я значения полей отделил табулятором (`t). И в конце так же добавил знак возврата каретки (`n), чтобы отделить между собой поля. Вывод в данном случае будет таким:
Computer Name: THOR Operating System: MicrosoftR Windows VistaT Business 32-bit Service Pack: Service Pack 1 installed
Т.е. поля идут с левого края и между собой отделены двумя пустыми строками. А значения полей отделены табулятором относительно того же левого края. Мне кажется, что такой выход весьма читабелен и данные не сливаются в кучу. Когда у поля только одно значение - всё просто. Но когда значений возможно несколько, то задача форматирования выхода таких данных стала для меня небольшой проблемой. Почему небольшой - потому что ответ посмотрел в блоге у Ричарда, а именно в посте W2KSG: Free Disk Space. Суть заключается в очень простом: берётся переменная с массивом данных и по конвейеру при помощи команды Format-Table подготавливается табличный выход и с указанием основного элемента, по которому будет этот выход формироваться. Чтобы лучше понять этот процесс я покажу его на примере вывода сведений о процессоре. Сейчас многопроцессорные системы не редкость и скрипт должен поддерживать показ сведений о более чем одном процессоре:
[user name] $CPU = gwmi Win32_Processor | Select Architecture, DeviceID, Name [user name] "Processors:" Processors: [user name] $CPU | ft DeviceID, @{Label = "Architecture"; Expression = {switch ($_.Architecture) { >> "0" {"x86"}; "1" {"MIPS"}; "2" {"Alpha"}; "3" {"PowerPC"}; "6" {"Intel Itanium"}; "9" {"x64"}}}}, >> @{Label = "Model"; Expression = {$_.name}} -AutoSize >> DeviceID Architecture Model -------- ------------ ----- CPU0 x86 Intel(R) Pentium(R) 4 CPU 2.60GHz CPU1 x86 Intel(R) Pentium(R) 4 CPU 2.60GHz [user name]
К сожалению, ноутбук у меня однопроцессорный, поэтому эту часть скрипта я запустил на стационаре (в котором, кстати, тоже только 1 физический процессор. Но технология Hyper-Threading эмулирует именно настоящую мультипроцессорность, а не логические ядра, как это сделано в многоядерных процессорах). Итак, давайте разберём строку:
$CPU | ft DeviceID, @{ Label = "Architecture"; Expression = { switch ($_.Architecture) { "0" {"x86"}; "1" {"MIPS"}; "2" {"Alpha"}; "3" {"PowerPC"}; "6" {"Intel Itanium"}; "9" {"x64"} } } }, @{Label = "Model"; Expression = {$_.name}} -AutoSize
Переменная $CPU содержит сведения о всех установленных процессорах в системе. Я эту переменную передал по конвейеру сразу на команду форматирования - ft (сокращённый алиас от Format-Table) и указал по какому свойству форматировать (DeviceID, который перечисляет ID номера всех процессоров начиная с 0). А дальше я использовал несколько хэш-таблиц для отображения дополнительных свойств каждого объекта. Параметр Label задаёт название новой графе, а Expression указывается значение параметра (как Architecture и Model). Если посмотреть справку по классу Win32_Processor, то можно увидеть, что свойство Architecture содержит лишь числовое значение (от 0 до 9) и в справке приведена расшифровка этих значений. Числовые значения в данном случае, согласитесь, не самый читабельный вариант. Поэтому в Expression я вместил конструкцию Switch, которая автоматически будет числовым значениям сопоставлять понятные текстовые значения. И если свойство Architecture вернёт числовое значение 0, то Switch в нашем случае сопоставит ему более понятное значение x86. И в последней строке я добавил ещё одну хэш-таблицу, которая добавляет ещё одну графу - Model, которая будет содержать свойство Name класса Win32_Processor - название процессора.
Такое форматирование выхода я делал для каждого поля, которое может содержать несколько значений. Например, объём каждого установленного модуля памяти:
$RAM = gwmi Win32_MemoryDevice | Select DeviceID, StartingAddress, EndingAddress
DeviceID будет содержать номер каждого установленного модуля памяти начиная с нулевого ряда (разбор понятий Ряд и Банк памяти выходит за рамки данного поста) или первого слота. StartingAddress и EndingAddress показывают адресное пространство, за которое отвечает каждый модуль начиная от первого байта. И простым вычитанием начального адреса из конечного мы получим ёмкость каждого модуля:
$RAM | ft DeviceID, @{Label = "Module Size(MB)"; Expression = { (($_.endingaddress - $_.startingaddress) / 1KB).ToString("F00")} } -AutoSize
Так же, как и с процессорами я содержимое переменной $RAM перенаправил по конвейеру на форматирование по столбцу DeviceID. И через запятую добавил ещё одну хэш-таблицу, которая будет показывать объём каждого модуля. Если просто произвести операцию вычитания, то мы получим объём памяти в килобайтах. Чтобы показать объём в мегабайтах я просто разделил полученную разность на килобайты - 1KB (Очень удобная штука :) ). Здесь важно было не ошибиться, т.к. по логике может показаться, что нужно делать на 1MB, чтобы получить размер в мегабайтах. Но, как я уже сказал выше, у нас разность будет уже в килобайтах. Поэтому, чтобы получить в мегабайты, то нам нужно разделить только на 1KB. И посмотрим, что мы будем иметь на выходе:
[vPodans] $RAM = gwmi Win32_MemoryDevice | Select DeviceID, StartingAddress, EndingAddress [vPodans] $RAM | ft DeviceID, @{Label = "Module Size(MB)"; Expression = {(($_.endingaddress - $_.startingaddress) / 1KB).ToString("F00")}} -AutoSize DeviceID Module Size(MB) -------- --------------- Memory Device 0 1024 Memory Device 1 1024 [vPodans]
Вот так мы получили номера модулей памяти и объём каждого из них. После деления у нас не получится целое число, поэтому я сконвертировал это число в строку (ToString) и указал количество знаков после запятой - 0 знаков (F00). В данном случае дробное число просто округляется до ближайшего целого числа. Количество знаков после запятой можно изменить, например указав F01, которое округлит число до ближайшего целого числа с точностью до 1 знака после запятой. Это всё не я придумал, а честно взял из поста W2KSG: Free Disk Space :)
Вот, в принципе, я рассказал о всех используемых в скрипте приёмах, которые доступны в PowerShell и при дальнейшей разработке скрипта я только выдёргивал нужные свойства каждого класса и формировал более-менее приличный и понятный вывод. Хотя в итоге я добавил ещё 2 вещи:
Таким образом извне можно подавать в функцию имена компьютеров (например, из текстового файла со списком имён компьютеров), что значительно расширяет функциональность скрипта. Ну и финальный скрипт:
######################################################## # Get-HwInfo.ps1 # Version 1.0 # # Getting basic information about systems hardware # # Vadims Podans (c) 2008 # http://vpodans.spaces.live.com/ ######################################################## function Get-HwInfo ($computers = ".") { $OS = gwmi Win32_OperatingSystem -ComputerName $computers | Select Caption, OSArchitecture, OtherTypeDescription, ServicePackMajorVersion, CSName, TotalVisibleMemorySize $CPU = gwmi Win32_Processor -ComputerName $computers | Select Architecture, DeviceID, Name $RAM = gwmi Win32_MemoryDevice -ComputerName $computers | Select DeviceID, StartingAddress, EndingAddress $MB = gwmi Win32_BaseBoard -ComputerName $computers | Select Manufacturer, Product, Version $VGA = gwmi Win32_VideoController -ComputerName $computers | Select Name, AdapterRam $HDD = gwmi Win32_DiskDrive -ComputerName $computers | select Model, Size $Volumes = gwmi Win32_LogicalDisk -Filter "MediaType = 12" -ComputerName $computers | Select DeviceID, Size, FreeSpace $CD = gwmi Win32_CDROMDrive | Select Id, Name, MediaType $NIC = gwmi Win32_NetworkAdapter -ComputerName $computers | ?{$_.NetConnectionID -ne $null} Write-Host "Computer Name: `n`t" $OS.CSName ` `n"Operating System: `n`" $OS.Caption " $OS.OtherTypeDescription $OS.OSArchitecture ` `nService Pack: `n`" Service Pack OS.ServicePackMajorVersion " in installed ` `nProcessors: $CPU | ft DeviceID, @{ Label = Architecture; Expression = { switch ($_.Architecture) { 0 {x86}; 1 {MIPS}; 2 {Alpha}; 3 {PowerPC}; 6 {Intel Itanium}; 9 {x64} } } }, @{Label = Model; Expression = {$_.name}} -AutoSize Write-Host Physical Memory: $RAM | ft DeviceID, @{Label = "ModuModule Size(MB)xpression = { (($_.endingaddress - $_.startingaddress) / 1KB).tostring("F00"F00)}} -AutoSize Write-Host Total Memory: `n`" ($OS.TotalVisibleMemorySize / 1KB).tostring() " M) ` ` `nMotherBoard: ` `n`Vendor: $MB.Manufacturer ` `n`Model: $MB.Product ` `n`Version: $MB.Version ` `nVideocontroller: ` `n`Model: $VGA.Name ` `n`Video RAM: ($VGA.AdapterRam/1MB).tostring(B`n" ) `n ` `nHarddDisks: $HDD | ft Model, @{Label=(GB)"; Expressi; Expression = {($_.Size/1GB).tostring(AutoS)}} -AutoSize Write-Host itions:" $Vol $Volumes | ft DeviceID, @{Label=(GB)"; Expressi; Expression={($_.Size/1GB).ToString( )}}, @{Label=GB)"; Expressi; Expression={($_.FreeSpace/1GB).tostring(AutoS)}} -AutoSize $CD | ft Id, @{Label = e"; Expressi; Expression = {$_.MediaType}}, @{Label = xpressi; Expression = {$_.Name}} -AutoSize Write-Host apters:" $NIC $NIC | ft NetConnectionID, @{ Label=tus"; Expressi; Expression = { switch ($_.NetConnectionStatus) { onn {ted"} } ect {g"} } ect {"} } onn {ting"} } war {not present"} } war {disabled"} } war {malfunction"} } a d {connected"} } ent {ating"} } ent {ation succeeded"} } hent {ation failed"} } alid {ddress"} } dent {ls required"} } } } }, @{Label=ressi; Expression={$_.name}} }
Здесь видно, что в конце я снова применил конструкцию Switch, которая расшифровывает числовое значение статуса сетевого адаптера в его текстовое значение.
Согласен, что скрипт выглядит не очень опрятно, но в PowerGUI он выглядит вполне сносно. На основе данного скрипта каждый может его с лёгкостью расширить и изменить под свои нужды, я лишь старался показать образец решения задачи, а так же показал некоторые интересные приёмы в PowerShell. Кстати говоря, данный скрипт можно отправить в HTML формат командой ConvertTo-Html. Это будет удобно, когда потребуется собрать подобные сведения с нескольких компьютеров. Тогда HTML формат будет весьма полезен для последующего анализа. На сегодня всё, а теперь спать.
Comments: