Несмотря на заложенные в PowerShell механизмы, благодаря которым вполне можно обходиться без обращений к WMI, большинство предпочитает идти по пути наименьшего сопротивления. Но вот незадача, простому пользователю попасть в чертоги WMI невозможно, а в некоторых случаях WMI поддразнивает самого системного администратора демонстрацией фиги при обращении к одному из своих классов, — прецеденты были и неоднократно, так что всецело уповать на WMI не стоит, да и знание альтернативных подходов к решению некоторых задач не будут лишними.
Справедливости ради стоит заметить, что определенные задачи могут быть вовсе решены на командном языке, однако со все более возрастающей популярностью PowerShell у некоторых складывается ложное впечатление, дескать, командная строка де-факто мертва. На самом деле командная строка хоть и отходит на второй план, списывать ее со счетов смысла не имеет, но не в виду привычки, а возможности в некоторых случаях упростить решение задачи. Например, как
PS C:\> ([Regex]'(?<=\").*(?=\")').Match(
>> (gp Registry::HKCR\http\shell\open\command).'(default)'
>> ).Value
C:\Program Files\Internet Explorer\iexplore.exe
PS C:\>
С учетом встроенных в командную строку assoc и ftype количество кода можно было бы сократить:
PS C:\> (cmd /c ftype (cmd /c assoc .htm).Split('=')[1]).Split('"')[1]
C:\Program Files\Internet Explorer\iexplore.exe
PS C:\>
Рассмотрим другой пример. Допустим, требуется получить uptime системы — задача, которая на первый взгляд неизбежно ведет к использованию WMI.
$w = gwmi Win32_OperatingSystem
'up {0} day{1}, {2:D2}:{3:D2}:{4:D2}' -f ($u = (Get-Date) - $w.ConvertToDateTime(
$w.LastBootUptime
)).Days, $(if ($u.Days -gt 1) {'s'}), $u.Hours, $u.Minutes, $u.Seconds
Ежели кто помнит такую штуку в командной строке, как typeperf, тот очевидно знает о счетчике «Время работы системы», приведение показаний которого к удобочитаемому виду будет выглядеть примерно следующим образом.
@echo off
setlocal & chcp 1251>nul
for /f "tokens=2 delims=," %%i in (
'typeperf "\Система\Время работы системы" -sc 1 ^| findstr /rc:"\:"'
) do set "sec=%%i"
set "sec=%sec:"=%"
for /f "tokens=1 delims=." %%i in ("%sec%") do set "sec=%%i"
set /a "ss=sec%%60", "sec/=60", "mm=sec%%60", "sec/=60", "hh=sec%%24", "dd=sec/24"
if %hh% lss 10 set "hh=0%hh%"
if %mm% lss 10 set "mm=0%mm%"
if %ss% lss 10 set "ss=0%ss%"
echo.up %dd% days, %hh%:%mm%:%ss%
endlocal & chcp 866>nul
exit /b
В переводе на PowerShell это будет выглядеть как:
$pc = New-Object Diagnostics.PerformanceCounter('System', 'System Up Time', $true)
[void]$pc.NextValue()
'up {0} day{1}, {2:D2}:{3:D2}:{4:D2}' -f (
$u = [TimeSpan]::FromSeconds($pc.NextValue())
).Days, $(if ($u.Days -gt 1) {'s'}), $u.Hours, $u.Minutes, $u.Seconds
Таким образом мы поймали сразу двух зайцев: обошли WMI и убедились в том, что пренебрегать командной строкой также не стоит, а если не убедились, то во всяком случае взяли ее на карандаш. К слову, есть и другие способы получения uptime (один из которых был приведен в первой части).
C# REPL в Linux: чем не PowerShell?
Вообще, сама по себе тема поиска альтернативных решений довольно обширна и вряд ли представляется возможным объять ее в каком-то небольшом обзоре, поэтому логичнее все свести к некой алгоритмической базе, способной стать отправной точкой в изысканиях. Но, как бы парадоксально это ни звучало, никаких алгоритмов в данном случае нет и быть не может, все в сущности своидится к чтению документации, узучению сборок платформы .NET с помощью дизассемблера вроде ILDASM и экспериментам с кодом. Если пояснять это на каких-то определенных примерах… хотелось бы сократить написание имен типов, тем самым снизив количество набираемых символов? Есть, конечно, ISE и Vim с автодополнением, но все же? В предыдущей части упомяналиcь ускорители типов — самая пора задействовать их.
Set-Content function:_from {
param(
[Parameter(Mandatory=$true, Position=0)]
[String]$NameSpace,
[Parameter(Mandatory=$true, Position=1)]
[ScriptBlock]$Import
)
$ret = $null
$arr = [accelerators]::Get.Keys
[Management.Automation.PSParser]::Tokenize($Import, [ref]$ret) | % {
if ($_.Type -match 'Command' -and $arr -notcontains $_.Content) {
[accelerators]::Add($_.Content, ("$($NameSpace).$($_.Content)"))
}
}
}
Поместив эту функцию также в профиль, получим что-то вроде Python импорта типов:
PS C:\> _from Runtime.InteropServices -import {
>> Marshal; HandleRef; GCHandle;
>> }
>>
PS C:\> [GCHandle]
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False GCHandle System.ValueType
PS C:\>
Согласитесь, гораздо проще и приятней, нежели набивать:
PS C:\> [Runtime.InteropServices.GCHandle]
...
PS C:\>