Дополнение и/или изменение команд с помощью прокси

Джеффри Сновер (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

Перевод: Виктор Горбунков