O que é o Windows PowerShell e o que ele come? Parte 3: passando parâmetros para scripts e funções, criando cmdlets



A segunda parte da série abordou os conceitos básicos da linguagem de programação PowerShell, mas agora vale a pena descobrir como usar o código nele escrito para tarefas administrativas. A maneira mais óbvia de fazer isso é executar o script. Além disso, é possível criar seus próprios cmdlets.

Índice:


Parâmetros posicionais
Bloco Param ()
Atributos de parâmetro adicionais
Passando parâmetros pelo pipeline
Estrutura do corpo da função
Atributo [CmdletBinding ()] e funções avançadas
Módulos de script e criação de cmdlets

Parâmetros posicionais


Em scripts e funções, você pode passar parâmetros posicionais (argumentos) cujos valores são gravados na variável interna $ args. Essa matriz unidimensional não requer declaração prévia e seu escopo é limitado a um script ou função. Por exemplo, execute o script mais simples:

Write-Host " :" $args.count
Write-Host ":" $args


imagem

Nas funções, os parâmetros posicionais são usados ​​da mesma maneira:

function Print-Args {
    Write-Host " :" $args.count
    Write-Host " 0:" $args[0]
    Write-Host " 1:" $args[1] 
}

Print-Args “Zero” “One”

Observe que, ao chamar Print-Args, não colocamos vírgula entre os parâmetros: não é um array que é passado para a função, mas valores individuais que são gravados no array unidimensional $ args - seu escopo é limitado pelo corpo da função.

imagem

O método descrito acima permite transferir qualquer número de parâmetros para um script ou função, mas ao chamar você deve seguir a ordem de sua sequência e pode acessá-los apenas pelo índice da matriz - isso nem sempre é conveniente.

Bloco Param ()


Em scripts e funções, é muito mais conveniente usar parâmetros nomeados. Em um artigo anterior, falamos sobre uma maneira de descrevê-los:

function test ($arg0, ..., $argN)
{
       
}

Uma sintaxe semelhante é familiar para os desenvolvedores, mas quando uma função é chamada, os parâmetros (se houver) são separados por espaços e não são colocados entre parênteses - uma certa dissonância surge. Essa é a especificidade das linguagens do shell: para trabalhar com o shell no modo interativo, os espaços entre os valores são muito mais convenientes. A chamada de teste ($ value0) também está correta, mas o parâmetro nesse caso é a expressão inteira entre parênteses, ou seja, ($ value0) em vez de $ value0 . Passando dessa maneira vários parâmetros não funcionarão. Como resultado da chamada de teste ($ value0, $ value1), a função receberá apenas um - uma matriz de dois elementos com os valores $ value0 e $ value1 .

A Microsoft Corporation recomenda o uso do bloco Param () - essa sintaxe é mais universal e permite especificar não apenas argumentos de função, mas também parâmetros de script:

param (
    $arg0, $arg1
)

Write-Host $arg0 $arg1


imagem

No corpo da função, fica assim:

function test {
     param (
           $arg0, $arg1
     )
}

Se a lista de argumentos da função for pequena, o bloco Param () desorganiza apenas o design, mas em muitos casos torna o código mais legível e é, entre outras coisas, um elemento de um bom estilo de programação.

Atributos de parâmetro adicionais


Ao descrever argumentos de função ou parâmetros de script, seus atributos adicionais podem ser configurados. O exemplo mais simples é uma instalação forçada do tipo:

param([int]$arg0)

ou

function test ([int]$arg0) {
}

Além da conversão de tipo, você pode usar o atributo [parameter ()]:

param(
    [parameter(Argument1=value1, Argument2=value2)]
    $ParameterName
)

Com sua ajuda, é fácil tornar o parâmetro obrigatório. Preste atenção ao uso simultâneo de vários atributos - nesse caso, eles se seguem:

param([parameter(Mandatory=$true)][int]$arg0)

ou

function test ([parameter(Mandatory=$true)][int]$arg0) { 
}

ou

function test {
          parameter([parameter(Mandatory=$true)][int]$arg0)
}


imagem

A posição permite especificar a ordem do parâmetro (por padrão, corresponde à ordem da declaração):

param(
                    [parameter(Mandatory=$true, Position=0)]
                    [int]
                    $arg0,

                    [parameter(Position=1)]
                    [string]
                    $arg1,

                    [parameter(Position=2)]
                    [array]
                    $arg2
)

O atributo [Parameter ()] possui outros argumentos, cuja lista completa está disponível no site da Microsoft. Outros atributos são descritos lá, com a ajuda dos quais você pode validar os valores passados, verificá-los usando expressões regulares, etc. Aqui estão alguns:

[Alias ​​()] define um alias para o parâmetro:

param(
    [parameter(Mandatory=$true)]
    [alias("ARG","ArgumentName")]
    [string[]]
    $arg0
)

O operador de conversão [string []] significa que o valor do parâmetro é uma matriz de string.

[AllowNull ()] permite $ null como um parâmetro necessário:

param(
    [parameter(Mandatory=$true)]
    [AllowNull()]
    [string]
    $arg0
)

[AllowEmptyString ()] permite uma string vazia como um parâmetro necessário:

param(
    [parameter(Mandatory=$true)]
    [AllowEmptyString()]
    [string]
    $arg0
)

[AllowEmptyCollection ()] permite uma matriz vazia como um parâmetro necessário:

param(
    [parameter(Mandatory=$true)]
    [AllowEmptyCollection()]
    [string[]]
    $arg0
)

Validação [ValidatePattern ()] usando uma expressão regular:

param(
    [parameter(Mandatory=$true)]
    [ValidatePattern("[0-9][0-9][0-9][0-9]")]
    [string[]]
    $arg0
)

[ValidateLength ()] verifica o comprimento de um parâmetro de string:

param(
    [parameter(Mandatory=$true)]
    [ValidateLength(1,10)]
    [string]
    $arg0
)

Passando parâmetros pelo pipeline


No primeiro artigo da série, falamos sobre a possibilidade de transferir dados para cmdlets por meio de um pipeline. No PowerShell, cmdlets e funções retornam objetos ou matrizes de objetos (resultados de instruções) e também os recebem na entrada. Para ver isso, preparamos um dos cmdlets usando Get-Help:

Get-Help Stop-Process -Parameter Name


imagem

Por meio do pipeline, você pode aceitar valores de parâmetros para os quais os atributos correspondentes estão definidos (ByValue e / ou ByPropertyName). No primeiro caso, o parâmetro será correspondido com um objeto recebido através do pipeline, desde que seu tipo corresponda ao esperado. No segundo parâmetro, o valor será a propriedade do objeto recebido cujo nome corresponde ao nome ou alias deste parâmetro. Para definir os atributos, use [parameter ()] com os argumentos booleanos ValueFromPipeline e ValueFromPipelineByPropertyName, cujo valor padrão é $ false:

param(
    [parameter(Mandatory=$true,
    ValueFromPipeline=$true)]
    [string[]]
    $Name
)

ou

param(
    [parameter(Mandatory=$true,
    ValueFromPipelineByPropertyName=$true)]
    [string[]]
    $Name
)

ValueFromPipelineByPropertyName geralmente é usado, se necessário, para passar vários parâmetros para que não haja confusão, e o argumento pode ser usado simultaneamente com ValueFromPipeline:

param(
    [parameter(Mandatory=$true,
    ValueFromPipeline=$true,
    ValueFromPipelineByPropertyName=$true)]
    [string[]]
    $Name
)

Write-Host $Name


imagem

Como você pode ver, os scripts também podem receber parâmetros através do pipeline, mas, no entanto, a aplicação prática dos atributos descritos acima é mais provável para funções avançadas , que serão discutidas abaixo.

Estrutura do corpo da função


No PowerShell, uma função pode incluir três blocos opcionais de código entre colchetes de instrução - Begin, Process e End. Parece algo como isto:

function test
{
   param()
   begin {}
   process {}
   end {}
}

O bloco Begin é o primeiro a ser executado uma vez e, se os parâmetros forem passados ​​para a função pelo pipeline, o código será executado antes que o primeiro objeto chegue ao processamento. As variáveis ​​$ _ e $ PSItem no bloco Begin nesse caso não conterão valores. Se a função for chamada usando parâmetros explicitamente especificados, eles estarão disponíveis no bloco Begin, pois não é necessário aguardar o recebimento de objetos do pipeline. Em seguida, o bloco Process é executado: se os parâmetros forem passados ​​pelo pipeline, ele será iniciado um por um para cada objeto. No caso de parâmetros especificados explicitamente, o bloco Process inicia apenas uma vez. A função termina executando o bloco End uma vez. Obviamente, o uso dessas construções é justificado apenas se a função puder receber objetos do pipeline:

function test
{

    param(
        [Parameter(ValueFromPipeline)]
        [string[]]
        $Param1,

        [string]$Param2
    )

    begin
    {
        Write-Host " Begin"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }

    process {
        Write-Host " Process"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }

    end
    {
        Write-Host " End"
        Write-Host "       ( pipeline):" $Param1
        Write-Host "       ( ):" $Param2
    }
}

'um', 'dois', 'três' | teste -Param2 'quatro'

imagem

Atributo [CmdletBinding ()] e funções avançadas


Para criar funções "avançadas" (e scripts, estritamente falando), você pode usar o atributo [ CmdletBinding () ]. Em particular, permite definir funções avançadas com os recursos de cmdlets binários compilados no Visual Studio, que são classes das classes .NET Core. Como esse atributo é usado principalmente em funções, vamos parar nelas:

function <Name>
{
    [CmdletBinding(ConfirmImpact=<String>,
    DefaultParameterSetName=<String>,
    HelpURI=<URI>,
    SupportsPaging=<Boolean>,
    SupportsShouldProcess=<Boolean>,
    PositionalBinding=<Boolean>)]

    Param ()

    Begin{}
    Process{}
    End{}
}

De fato, [CmdletBinding ()] inicializa uma nova instância da classe CmdletBindingAttribute chamando um construtor para o qual argumentos opcionais podem ser transmitidos. Sua descrição detalhada está no site da Microsoft. O atributo CmdletBinding permite controlar recursos adicionais da função avançada: adicionar suporte para -Confirm e -WhatIf (via SupportsShouldProcess), -Force, -Verbose e -Debug, além de desativar a ligação de parâmetro posicional, etc. Além disso, analisaremos o uso de parâmetros especiais.

O parâmetro -Force é usado para suprimir solicitações para várias operações;

-E senecessário para emular o lançamento e exibir informações sobre as consequências da execução de uma função (comando) sem esse parâmetro. Geralmente usado se uma função pode executar ações destrutivas.

O item Remover C: \ Windows \ notepad.exe -WhatIf

imagem

-Confirm requer confirmação e também é usado se a função puder executar ações destrutivas.

function Delete-File {
[CmdletBinding(
    ConfirmImpact = 'High',
    SupportsShouldProcess = $true
)]
    param(
        [string]$File,
        [switch]$Force
    )
    if ($Force -or $PSCmdlet.ShouldProcess($File,"Delete file")) {
        Remove-Item $File
    }
}


imagem

Para processar -WhatIf e / ou -Confirm, é chamado o método ShouldProcess (SupportsShouldProcess = $ true), que solicita ou emula a execução do comando. Para implementar o processamento -Force, colocamos em primeiro lugar na condição SE. Primeiro, a expressão à esquerda do operador -ou é verificada e, se for verdadeira, o teste é interrompido - o método ShouldProcess não será chamado. Além disso, no atributo [CmdletBinding ()], especificamos o argumento ConfirmImpact, que determina o nível de influência do código no sistema e inclui o manipulador de parâmetros -Confirm. Este argumento pode assumir os seguintes valores:

Nenhum ou não especificado - as mensagens de confirmação não serão exibidas, mesmo se o parâmetro -Confirm for passado.

Baixa - a função afeta levemente o sistema e não cria riscos significativos de perda de dados.

Média - exposição média com pouco risco de perda de dados como resultado de ações destrutivas.

Alto - o código cria um alto risco de perda de dados como resultado de ações destrutivas.

Por padrão, para uma sessão do PowerShell, o nível de exposição é considerado Alto. O valor atual é armazenado na variável $ ConfirmPreference e, se o código tiver o mesmo ou maior nível de impacto no sistema, uma solicitação de confirmação sempre será exibida.

Opções -Verbose e -Debugnecessário para exibir informações de depuração. Seu uso é considerado um bom estilo de programação (esqueça o Write-Host, isso não é necessário em funções avançadas). O primeiro parâmetro exibe informações sobre o progresso e o segundo - informações detalhadas sobre depuração. Também possibilita alternar para a execução de código passo a passo. O comportamento de -Verbose e -Debug é definido assim:

function Get-Something {
[CmdletBinding()]
    param()
    if ($PSBoundParameters.Verbose) {$VerbosePreference = "Continue"}
    if ($PSBoundParameters.Debug) {$DebugPreference = "Continue"}
    Write-Verbose "Type some verbose information"
    Write-Debug "Type some debug information"
}

Para trabalhar com parâmetros especiais, usamos a variável $ PSBoundParameters. Por padrão, os valores de $ VerbosePreference e $ DebugPreference são iguais a 'SilentlyContinue'; portanto, mesmo que os parâmetros correspondentes sejam especificados, as informações de depuração não serão exibidas - elas devem ser transferidas para o estado 'Continue'.

Módulos de script e criação de cmdlet


Vamos começar a criar nossos próprios cmdlets. De fato, essas são funções avançadas descritas nos chamados módulos de script - arquivos de texto com a extensão .psm1. Eles são armazenados em diretórios definidos na variável de ambiente PSModulePath. Você pode exibir os caminhos para eles usando o seguinte comando:

Get-ChildItem Env: \ PSModulePath | Format-Table -AutoSize

O conjunto padrão se parece com:

C: \ Users \% UserName% \ Documents \ WindowsPowerShell \ Modules
C: \ Arquivos de programas \ WindowsPowerShell \ Modules
C: \ Windows \ System32 \ WindowsPowerShell \ v1.0 \ Modules

Depois de criar o arquivo ModuleName.psm1 com a função Delete-File estendida da seção anterior, você precisa salvá-lo, por exemplo, em] C: \ Users \% UserName% \ Documents \ WindowsPowerShell \ Modules \ ModuleName. Observe que o módulo de script deve ser armazenado em um subdiretório separado, cujo nome coincide com o nome base (sem extensão) do arquivo .psm1. Depois de executar o comando Import-Module ModuleName , a função Delete-File ficará disponível para o usuário e, como é avançada, do ponto de vista prático, é o mesmo cmdlet.

imagem

Neste artigo, examinamos em detalhes a passagem de parâmetros para funções e scripts. A próxima parte da série se concentrará na programação orientada a objetos.

Parte 1: Windows PowerShell Essentials
2: Windows PowerShell
4: ,



All Articles