Вторая версия PowerShell во многом определила дальнейшее развитие последнего. Этому главным образом способствовало введение понятия модуля и возможность писать сценарии по функциональной нагрузке схожие с полноценными командлетами. Несмотря на то, что PowerShell дорос уже до пятой версии, некоторые до сих пор считают вторую эталонной, хотя в ней имеется достаточно упущений, не считая некоторых багов. Всем тем, кто по-прежнему считает вторую версию лучшей и посвящен этот пост.
Хотя поминать второй PowerShell все же еще рановато.
Как известно, операторы побитового сдвига влево (-shl) и вправо (-shr) появились в PowerShell начиная с третьей версии, к слову, не самой удачной. До этого некоторые администраторы и, что более удивительно, программисты негодовали на данное упущение, правда находились из них бившие себя пяткой в грудь, дескать, проблема решается следующим образом:
Совершенно очевидно, что каждый из вышеобозначенных пяточников просто тупо копипастил код, не удосужившись припомнить математику. Ибо что есть побитовый сдвиг на одну позицию влево с точки зрения последней? Правильно — умножение на 2. Иными словами запись 7 -shl 1 можно представить:
А 7 -shl 2:
И т.д. Сдвиг вправо на одну позицию противоположен сдвигу влево, т.е. эквивалентен делению на 2 с отбрасываением остатка от деления.
Иными словами, создание сборки на этом фоне кажется не то, что неубедительным, а просто недоразумением. Идеологически правильным было бы вообще определить динамический метод с использованием MSIL, в переводе на который сдвиг представляется как:
Концептуально это будет выглядеть так:
Хотя поминать второй 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
План книги, о которой была речь в предыдущем посте, составлен и в черновом варианте, в котором он и прибывает, пока что выглядит так.
Все это планируется рассматривать с учетом x64, однако пересчитав свои скромные сбережения, выяснилось — тот максимум, на который их хватит не шибко велик и в лучшем случае составит месяц. По этой причине, если конечно вдруг не произойдет чуда или кто-то не изъявит поддержать материально, работа над книгой приостанавливается. Если есть желание все же прочесть в будущем книгу, можете начинать сбор подписей (а вместе с ними и средств :) на ее создание. Адрес для сбора подписей powershellbook@mail.ru
- Внутреннее устройство PowerShell
- Командлеты и модули
- Типы и рефлексия
- Использование MSIL в PowerShell
- Все о PInvoke
- Приложения
Все это планируется рассматривать с учетом x64, однако пересчитав свои скромные сбережения, выяснилось — тот максимум, на который их хватит не шибко велик и в лучшем случае составит месяц. По этой причине, если конечно вдруг не произойдет чуда или кто-то не изъявит поддержать материально, работа над книгой приостанавливается. Если есть желание все же прочесть в будущем книгу, можете начинать сбор подписей (а вместе с ними и средств :) на ее создание. Адрес для сбора подписей powershellbook@mail.ru
Поделиться с друзьями
PsyHaSTe
Не совсем понял, о чем статья. Что в powershell плохо определен сдвиг?
Ну и что касается решения:
Только вот не нужно забывать, что таким образом мы привносим работу с плавающей запятой, и не задаваясь вопросами производительности, вполне можем получить 1610612735.9999999998 вместо корректного ответа.
GrigoriZakharov
Спасибо за дополнение, думал об этом упомянуть, но так как пост лишь своего рода обобщение, то не стал этого делать.