Вторая версия PowerShell во многом определила дальнейшее развитие последнего. Этому главным образом способствовало введение понятия модуля и возможность писать сценарии по функциональной нагрузке схожие с полноценными командлетами. Несмотря на то, что PowerShell дорос уже до пятой версии, некоторые до сих пор считают вторую эталонной, хотя в ней имеется достаточно упущений, не считая некоторых багов. Всем тем, кто по-прежнему считает вторую версию лучшей и посвящен этот пост.
Хотя помянать второй PowerShell все же еще рановато.
Операторы -shl и -shr
Как известно, операторы побитового сдвига влево (-shl) и вправо (-shr) появились в PowerShell начиная с третьей версии, к слову, не самой удачной. До этого некоторые администраторы и, что более удивительно, программисты негодовали на данное упущение, правда находились из них бившие себя пяткой в грудь, дескать, проблема решается следующим образом:
Add-Type @"
public class Shift {
public static int Right(int x, int count) { return x >> count; }
public static uint Right(uint x, int count) { return x >> count; }
public static long Right(long x, int count) { return x >> count; }
public static ulong Right(ulong x, int count) { return x >> count; }
public static int Left(int x, int count) { return x << count; }
public static uint Left(uint x, int count) { return x << count; }
public static long Left(long x, int count) { return x << count; }
public static ulong Left(ulong x, int count) { return x << count; }
}
"@
Совершенно очевидно, что каждый из вышеобозначенных пяточников просто тупо копипастил код, не удосужившись припомнить математику. Ибо что есть побитовый сдвиг на одну позицию влево с точки зрения последней? Правильно — умножение на 2. Иными словами запись 7 -shl 1 можно представить:
PS E:> 7 * [Math]::Pow(2, 1)
14
А 7 -shl 2:
PS E:> 7 * [Math]::Pow(2, 2)
28
И т.д. Сдвиг вправо на одну позицию противоположен сдвигу влево, т.е. эквивалентен делению на 2 с отбрасываением остатка от деления.
PS E:> [Math]::Floor(7 / [Math]::Pow(2, 1))
3
Иными словами, создание сборки на этом фоне кажется не то, что неубедительным, а просто недоразумением.
Альтернативный способ — определить динамический метод с использованием MSIL, в переводе на который сдвиг представляется как:
Ldarg_0
Ldarg_1
Ldc_I4_S, 31
And
Shl //или Shr, если сдвигать нужно вправо
Ret
Концептуально это будет выглядеть так:
#акселераторы
@(
[Reflection.Emit.DynamicMethod],
[Reflection.Emit.OpCodes]
) | ForEach-Object {
$keys = ($ta = [PSObject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
))::Get.Keys
$collect = @()
}{
if ($keys -notcontains $_.Name) {
$ta::Add($_.Name, $_)
}
$collect += $_.Name
}
function Set-ShiftMethod {
param(
[Parameter()]
[ValidateNotNullOrEmpty()]
[ValidateSet('Left', 'Right')]
[String]$X = 'Left'
)
#массив опкодов - не будем же мы писать несколько методов
#из-за разницы всего в одном опкоде, верно?!
@(
'Ldarg_0'
'Ldarg_1'
'Ldc_I4_S, 31'
'And'
$(if ($X -eq 'Right') { 'Shr' } else { 'Shl' })
'Ret'
) | ForEach-Object {
#наш динамический метод
$def = New-Object DynamicMethod(
$X, [Int32], @([Int32], [Int32])
)
$il = $def.GetILGenerator()
}{
if ($_ -notmatch ',') { $il.Emit([OpCodes]::$_) }
else {$il.Emit(
[OpCodes]::(($$ = $_.Split(','))[0]), [Int32]($$[1].Trim())
)}
}
#создаем делегат
$def.CreateDelegate([Func[Int32, Int32, Int32]])
}
#примеры
(Set-ShiftMethod Left).Invoke(7, 3) #7 -shl 3 -> 56
(Set-ShiftMethod Right).Invoke(7, 1) #7 -shr 1 -> 3
#удаляем акселераторы
$collect | ForEach-Object { [void]$ta::Remove($_) }
- Внутреннее устройство PowerShell
- Командлеты и модули
- Типы и рефлексия
- Использование MSIL в PowerShell
- Все о PInvoke
- Приложения
Все это планируется рассматривать с учетом x64, однако пересчитав свои скромные сбережения, выяснилось — тот максимум, на который их хватит не шибко велик и в лучшем случае составит месяц. По этой причине, если конечно вдруг не произойдет чуда или кто-то не изъявит поддержать материально, работа над книгой приостанавливается. Если есть желание все же прочесть в будущем книгу, можете начинать сбор подписей (а вместе с ними и средств :) на ее создание. Адрес для сбора подписей powershellbook@mail.ru
Автор: GrigoriZakharov