Дополнение и/или изменение команд с помощью прокси
Джеффри Сновер (Jeffrey Snover) [MSFT]
Windows Management Partner Architect
Посетите английский блог команды Windows PowerShell: https://blogs.msdn.com/PowerShell
Посетите Windows PowerShell ScriptCenter: https://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
В V2 имеется много мощных возможностей, о которых трудно узнать с самого начала. Эта возможность должна снести вам крышу, когда вы поймете, что она позволяет делать.
В этом блоге я я уже писал о прокси-коммандлетах, которые предоставляют возможность из одного командлета вызывать другой. Вы всегда могли делать это, но делать это правильно всегда было очень сложно. В частности, если вы хотели контролировать выполнение вызванной команды – контролировать, когда будут вызваны ее методы BEGINPROCESS(), PROCESSRECORD(), ENDPROCESS() и др., так, чтобы они демонстрировали правильное поведение. То, что нам было нужно – «Пошаговый конвейер». Вы знаете, что эта возможность появилось в CTP3. Теперь битогрызы (bit-biters - те кто желает разобраться в деталях) , захотят изучить пошаговые конвейеры в мельчайших подробностях, поскольку они могут быть одновременно мощными и сложными. – НО я хочу объяснить вам то что КАЖДЫЙ может и должен использовать их весьма эффективно, просто следуя рецепту, который я собираюсь предложить вам в этом блоге и прикрепленном модуле.
Сценарий 1: Удаление функций Представьте себе вариант, который рассматривался в Configuring PowerShell for Remoting. Вы создаете конфигурацию, которая дает людям доступ к некоторым, но не всем, командам. Вы можете также захотеть дополнительно ограничить команды, которые разрешаете.
Сценарий 2: Добавление функций В V2 CTP3 мы добавляем к Get-Process параметры -FileVersionInfo и -Module (знаете почему? Остановитесь на минуту и попробуйте их – они очень хороши/полезны. Попробуйте так: gps powershell -FileVersionInfo |fl * ). Благодаря прокси, вы можете самостоятельно легко делать подобные вещи.
Концепция Давайте потратим минуту и сфокусируем свое внимание на нескольких важных вещах. Командлеты PowerShell являются классами .NET с атрибутами у параметров. ПОЧЕМУ? Потому что это позволяет нам знать о них и ИЗВЛЕКАТЬ ИХ МЕТАДАННЫЕ (их грамматику). Затем мы можем использовать эту грамматику для организации синтаксиса командной строки. Это магия PowerShell. Дальше вы увидите, как мы делаем с ними всевозможные отличные штуки (например, раннее связывание интерфейса WebService, порождение рано связываемых строго типизированых свойст классов (аксессоров) C#, автоматическая генерация пользовательского интерфейса, и т.д. и т.п.). Вот главные вещи, которые мы делаем в V2:
1) Мы раскрываем метаданные. Вы можете создать New-Object в System.Management.Automation.CommandMetaData, передав ему cmdletInfo, и получить его метаданные. Сделайте так:
PS> New-Object System.Management.Automation.CommandMetaData (gcm Get-Process)
2) Мы делаем метаданные программируемыми . Вы можете добавлять/удалять параметры, изменять параметры, изменять имя и т.д.
3) Мы используем метаданные для порождения сценария Cmdlet.
PS> $metaData = New-Object System.Management.Automation.CommandMetaData (gcm Get-Process)
PS> [System.Management.Automation.ProxyCommand]::create($MetaData)
Для разработчиков и умелых составителей сценариев все это совершенно ясно, но может быть для новичков это стоит объяснить более доступно. Значит ли это, что новички в сценариях не смогут этим воспользоваться? КОНЕЧНО, НЕТ! Используя PowerShell, люди создают абстракции для использования группами других людей. Умелые авторы сценариев часто создают абстракции, которые позволяют менее умелым пользователям делать вещи, которые они не смогли бы сделать самостоятельно. Именно для этого и существует наш блог. Я прикрепил к статье модуль MetaProgramming, который (к счастью) делает это более простой задачей.
<#
You are responsible for implementing the logic for added parameters. These
parameters are bound to $PSBoundParameters so if you pass them on the the
command you are proxying, it will almost certainly cause an error. This logic
should be added to your BEGIN statement to remove any specified parameters
from $PSBoundParameters.
In general, the way you are going to implement additional parameters is by
modifying the way you generate the $scriptCmd variable. Here is an example
of how you would add a -SORTBY parameter to a cmdlet:
if ($SortBy)
{
[Void]$PSBoundParameters.Remove("SortBy")
$scriptCmd = {& $wrappedCmd @PSBoundParameters |Sort-Object -Property $SortBy}
}else
{
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
}
################################################################################
New ATTRIBUTES
if ($SortBy)
{
[Void]$PSBoundParameters.Remove("SortBy")
}
################################################################################
#>
PS> Import-Module MetaProgramming
PS> Get-Command -Module MetaProgramming
CommandType Name Definition
----------- ---- ----------
Function New-ParameterAttribute ...
Function New-ProxyCommand ...
PS> Get-Help New-ProxyCommand -Detailed
NAME
New-ProxyCommand
SYNOPSIS
Generate a script for a ProxyCommand to call a base Cmdlet adding or re
moving parameters.
SYNTAX
New-ProxyCommand [[-Name] [<String>]] [[-CommandType] [<CommandTypes>]]
[[-AddParameter] [<String[]>]] [[-RemoveParameter] [<String[]>]] [[-Ad
dParameterAttribute] [<Hashtable>]] [<CommonParameters>]
DETAILED DESCRIPTION
This command generates command which calls another command (a ProxyComm
and).
In doing so, it can add additional attributes to the existing parameter
s.
This is useful for things like enforcing corporate naming standards.
It can also ADD or REMOVE parameters. If you ADD a parameter, you'll h
ave
to implement the semantics of that parameter in the code that gets gene
rated.
PARAMETERS
-Name
Name of the Cmdlet to proxy.
-CommandType
Type of Command we are proxying. In general you dont' need to specify this but
it becomes necessary if there is both a cmdlet and a function with
the same
name
-AddParameter
List of Parameters you would like to add. NOTE - you have to edit t
he resultant
code to implement the semantics of these parameters. ALSO - you ne
ed to remove
them from $PSBOUND
-RemoveParameter
-AddParameterAttribute
<CommonParameters>
This cmdlet supports the common parameters: -Verbose, -Debug,
-ErrorAction, -ErrorVariable, -WarningAction, -WarningVariable,
-OutBuffer and -OutVariable. For more information, type,
"get-help about_commonparameters".
-------------------------- EXAMPLE 1 --------------------------
New-ProxyCommand get-process -typ all -RemoveParameter FileVersionInfo,
Module,ComputerName -AddParameter SortBy > c:\ps\get-myprocess.ps1
REMARKS
To see the examples, type: "get-help New-ProxyCommand -examples".
For more information, type: "get-help New-ProxyCommand -detailed".
For technical information, type: "get-help New-ProxyCommand -full".
Давайте посмотрим небольшой пример, чтобы узнать, насколько это может быть просто:
PS>New-ProxyCommand get-process -RemoveParameter ID > .\get-myprocess.ps1
PS>.\Get-MyProcess -Name lsass
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
1126 17 5796 11744 37 97.64 488 lsass
PS>.\Get-MyProcess -ID 488
C:\temp\get-myprocess.ps1 : A parameter cannot be found that matches parame
ter name 'ID'.
At line:1 char:20
+ .\Get-MyProcess -ID <<<< 488
+ CategoryInfo : InvalidArgument: (:) [get-myprocess.ps1], Pa
rameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,get-myprocess.ps1
Добавлять параметры немного сложнее, но если вы выполните эти шаги – это достаточно просто (может показаться, что я вас пугаю, но верьте мне – просто сделайте это несколько раз, и вы увидите, что это потрясающе просто.). Как узнать, какие это шаги? Я поместил их в сценарий. Если вы выполните следующую команду:
PS> New-ProxyCommand get-process -AddParameter SortBy > .\get-myprocess.ps1
PS> Powershell_ise .\get-myprocess.ps1
То вот что вы увидите в начале этого файла:
<#
You are responsible for implementing the logic for added parameters. These
parameters are bound to $PSBoundParameters so if you pass them on the the
command you are proxying, it will almost certainly cause an error. This logic
should be added to your BEGIN statement to remove any specified parameters
from $PSBoundParameters.
In general, the way you are going to implement additional parameters is by
modifying the way you generate the $scriptCmd variable. Here is an example
of how you would add a -SORTBY parameter to a cmdlet:
if ($SortBy)
{
[Void]$PSBoundParameters.Remove("SortBy")
$scriptCmd = {& $wrappedCmd @PSBoundParameters |Sort-Object -Property $SortBy}
} else
{
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
}
################################################################################
New ATTRIBUTES
if ($SortBy)
{
[Void]$PSBoundParameters.Remove("SortBy")
}
################################################################################
#>
Примечание – Код New-ProxyCommand должен порождать шаблон HELP для прокси, но у меня не было времени это реализовать. Извините.
Не скучайте!
Джеффри Сновер (Jeffrey Snover) [MSFT]
Windows Management Partner Architect
Посетите английский блог команды Windows PowerShell: https://blogs.msdn.com/PowerShell
Посетите Windows PowerShell ScriptCenter: https://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx
Перевод: Виктор Горбунков