Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Os one-liners e scripts do PowerShell que precisam ser modificados com frequência são bons candidatos para se transformar em funções reutilizáveis.
Escreva funções sempre que possível porque são mais orientadas para ferramentas. Você pode adicionar as funções a um módulo de script, colocar esse módulo em um local definido no $env:PSModulePath
e chamar as funções sem precisar localizar onde você salvou as funções. Usando o módulo PowerShellGet, é fácil compartilhar seus módulos do PowerShell em um repositório NuGet.
PowerShellGet é fornecido com o PowerShell versão 5.0 e superior. Também está disponível como um download separado para o PowerShell versão 3.0 e superior.
Não complique demais as coisas. Mantenha a simplicidade e use a maneira mais direta de realizar uma tarefa. Evite aliases e parâmetros posicionais em qualquer código que você reutilizar. Formate seu código para facilitar a leitura. Não codifice valores; usar parâmetros e variáveis. Não escreva código desnecessário, mesmo que não prejudique nada. Acrescenta complexidade desnecessária. A atenção aos detalhes percorre um longo caminho ao escrever qualquer código do PowerShell.
Nomeação
Ao nomear as suas funções no PowerShell, use um nome em PascalCase com um verbo permitido e um substantivo singular. Para obter uma lista de verbos aprovados no PowerShell, execute Get-Verb
. O exemplo a seguir classifica os resultados de Get-Verb
pela propriedade Verb.
Get-Verb | Sort-Object -Property Verb
A propriedade Group dá uma ideia de como os verbos devem ser usados.
Verb Group
---- -----
Add Common
Approve Lifecycle
Assert Lifecycle
Backup Data
Block Security
Checkpoint Data
Clear Common
Close Common
Compare Data
Complete Lifecycle
Compress Data
Confirm Lifecycle
Connect Communications
Convert Data
ConvertFrom Data
ConvertTo Data
Copy Common
Debug Diagnostic
Deny Lifecycle
Disable Lifecycle
Disconnect Communications
Dismount Data
Edit Data
Enable Lifecycle
Enter Common
Exit Common
Expand Data
Export Data
Find Common
Format Common
Get Common
Grant Security
Group Data
Hide Common
Import Data
Initialize Data
Install Lifecycle
Invoke Lifecycle
Join Common
Limit Data
Lock Common
Measure Diagnostic
Merge Data
Mount Data
Move Common
New Common
Open Common
Optimize Common
Out Data
Ping Diagnostic
Pop Common
Protect Security
Publish Data
Push Common
Read Communications
Receive Communications
Redo Common
Register Lifecycle
Remove Common
Rename Common
Repair Diagnostic
Request Lifecycle
Reset Common
Resize Common
Resolve Diagnostic
Restart Lifecycle
Restore Data
Resume Lifecycle
Revoke Security
Save Data
Search Common
Select Common
Send Communications
Set Common
Show Common
Skip Common
Split Common
Start Lifecycle
Step Common
Stop Lifecycle
Submit Lifecycle
Suspend Lifecycle
Switch Common
Sync Data
Test Diagnostic
Trace Diagnostic
Unblock Security
Undo Common
Uninstall Lifecycle
Unlock Common
Unprotect Security
Unpublish Data
Unregister Lifecycle
Update Data
Use Other
Wait Lifecycle
Watch Common
Write Communications
É importante usar um verbo aprovado para suas funções do PowerShell. Os módulos que contêm funções com verbos não aprovados geram uma mensagem de aviso quando são importados para uma sessão do PowerShell. Essa mensagem de aviso faz com que as suas funções não pareçam profissionais. Verbos não aprovados também limitam a visibilidade das suas funcionalidades.
Uma função simples
Uma função no PowerShell é declarada com a palavra-chave em inglês "function", seguida pelo nome da função e por chavetas de abertura e fechamento ({ }
). O código executado pela função está contido nestas chavetas.
function Get-Version {
$PSVersionTable.PSVersion
}
A função mostrada no exemplo a seguir é um exemplo simples que retorna a versão do PowerShell.
Get-Version
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Quando você usa um nome genérico para suas funções, como Get-Version
, isso pode causar conflitos de nomenclatura. Comandos padrão adicionados no futuro ou comandos que outros possam escrever podem entrar em conflito com eles. Prefira adicionar um prefixo nominal aos nomes das suas funções para ajudar a evitar conflitos de nomenclatura. Por exemplo: <ApprovedVerb>-<Prefix><SingularNoun>
.
O exemplo a seguir usa o prefixo PS
.
function Get-PSVersion {
$PSVersionTable.PSVersion
}
Além do nome, esta função é idêntica à anterior.
Get-PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Você ainda pode ter um conflito de nome mesmo quando adiciona um prefixo ao substantivo. Gosto de prefixar os meus substantivos de função com as minhas iniciais. Desenvolva um padrão e cumpra-o.
function Get-MrPSVersion {
$PSVersionTable.PSVersion
}
Essa função não é diferente das duas anteriores, exceto para usar um nome mais exclusivo para tentar evitar conflitos de nomenclatura com outros comandos do PowerShell.
Get-MrPSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Uma vez carregado na memória, você pode ver as funções no Function PSDrive.
Get-ChildItem -Path Function:\Get-*Version
CommandType Name Version
----------- ---- -------
Function Get-Version
Function Get-PSVersion
Function Get-MrPSVersion
Se você quiser remover essas funções da sessão atual, remova-as do Função PSDrive ou feche e reabra o PowerShell.
Get-ChildItem -Path Function:\Get-*Version | Remove-Item
Verifique se as funções foram realmente removidas.
Get-ChildItem -Path Function:\Get-*Version
Se as funções foram carregadas como parte de um módulo, você pode descarregar o módulo para removê-las.
Remove-Module -Name <ModuleName>
O cmdlet Remove-Module
remove os módulos do PowerShell da memória em sua sessão atual do PowerShell. Ele não os remove do seu sistema ou disco.
Parâmetros
Não atribua valores estaticamente. Em vez disso, use parâmetros e variáveis. Ao nomear seus parâmetros, use o mesmo nome que os cmdlets padrão para seus nomes de parâmetros sempre que possível.
Na função a seguir, observe que usei ComputerName e não Computer, ServerNameou Host para o nome do parâmetro. O uso de ComputerName padroniza o nome do parâmetro para combinar com o nome e a capitalização do parâmetro, como nos cmdlets padrão.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
A função a seguir consulta todos os comandos em seu sistema e retorna o número com nomes de parâmetros específicos.
function Get-MrParameterCount {
param (
[string[]]$ParameterName
)
foreach ($Parameter in $ParameterName) {
$Results = Get-Command -ParameterName $Parameter -ErrorAction SilentlyContinue
[pscustomobject]@{
ParameterName = $Parameter
NumberOfCmdlets = $Results.Count
}
}
}
Como você pode ver nos resultados a seguir, 39 comandos que têm um ComputerName parâmetro. Não há comandos com parâmetros como Computer, ServerName, Hostou Machine.
Get-MrParameterCount -ParameterName ComputerName, Computer, ServerName,
Host, Machine
ParameterName NumberOfCmdlets
------------- ---------------
ComputerName 39
Computer 0
ServerName 0
Host 0
Machine 0
Use o mesmo caso para seus nomes de parâmetro que os cmdlets padrão. Por exemplo, use ComputerName
, não computername
. Esse esquema de nomenclatura ajuda as pessoas familiarizadas com o PowerShell a descobrir as suas funções, parecendo e funcionando como os cmdlets padrão.
A instrução param
permite definir um ou mais parâmetros. Uma vírgula (,
) separa as definições de parâmetros. Consulte about_Functions_Advanced_Parameterspara obter mais informações.
Funções avançadas
Transformar uma função em uma função avançada no PowerShell é simples. Uma das diferenças entre uma função e uma função avançada é que as funções avançadas têm parâmetros comuns que são adicionados automaticamente. Os parâmetros comuns incluem parâmetros como Verbose e Debug.
Comece com a função Test-MrParameter
que foi usada na seção anterior.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
Existem algumas maneiras diferentes de ver os parâmetros comuns. Uma forma é visualizar a sintaxe com Get-Command
.
Get-Command -Name Test-MrParameter -Syntax
Observe que a função Test-MrParameter
não tem parâmetros comuns.
Test-MrParameter [[-ComputerName] <Object>]
Outra é aprofundar a propriedade parâmetros de Get-Command
.
(Get-Command -Name Test-MrParameter).Parameters.Keys
ComputerName
Adicione o atributo CmdletBinding
para transformar a função em uma função avançada.
function Test-MrCmdletBinding {
[CmdletBinding()] # Turns a regular function into an advanced function
param (
$ComputerName
)
Write-Output $ComputerName
}
Quando você especifica CmdletBinding
, os parâmetros comuns são adicionados automaticamente.
CmdletBinding
requer um bloco param
, mas o bloco param
pode estar vazio.
Get-Command -Name Test-MrCmdletBinding -Syntax
Test-MrCmdletBinding [[-ComputerName] <Object>] [<CommonParameters>]
Aprofundar na propriedade 'parâmetros' de Get-Command
mostra os nomes reais dos parâmetros, incluindo os comuns.
(Get-Command -Name Test-MrCmdletBinding).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
SuportesDevemProcessar
O atributo SupportsShouldProcess
adiciona os parâmetros de mitigação de risco WhatIf e Confirm. Esses parâmetros são necessários apenas para comandos que fazem alterações.
function Test-MrSupportsShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param (
$ComputerName
)
Write-Output $ComputerName
}
Repare que agora há parâmetros WhatIf e Confirm.
Get-Command -Name Test-MrSupportsShouldProcess -Syntax
Test-MrSupportsShouldProcess [[-ComputerName] <Object>] [-WhatIf] [-Confirm]
[<CommonParameters>]
Mais uma vez, você também pode usar Get-Command
para retornar uma lista dos nomes de parâmetros reais, incluindo os comuns, juntamente com WhatIf e Confirm.
(Get-Command -Name Test-MrSupportsShouldProcess).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
WhatIf
Confirm
Validação de parâmetros
Valide a entrada logo no início. Não permita que seu código continue em um caminho quando ele não puder ser concluído sem uma entrada válida.
Sempre especifique um tipo de dados para as variáveis usadas para parâmetros. No exemplo a seguir, String é especificado como o tipo de dados para o parâmetro ComputerName. Essa validação limita a que apenas um único nome de computador seja especificado para o parâmetro ComputerName.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[string]$ComputerName
)
Write-Output $ComputerName
}
Um erro será gerado se mais de um nome de computador for especificado.
Test-MrParameterValidation -ComputerName Server01, Server02
Test-MrParameterValidation : Cannot process argument transformation on
parameter 'ComputerName'. Cannot convert value to type System.String.
At line:1 char:42
+ Test-MrParameterValidation -ComputerName Server01, Server02
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Test-MrParameterValidation]
, ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Test-MrP
arameterValidation
O problema com a definição atual é que é válido omitir o valor do parâmetro ComputerName, mas um valor é necessário para que a função seja concluída com êxito. Este cenário é onde o atributo de parâmetro Mandatory
é benéfico.
A sintaxe usada no exemplo a seguir é compatível com o PowerShell versão 3.0 e superior.
[Parameter(Mandatory=$true)]
pode ser especificado para tornar a função compatível com o PowerShell versão 2.0 ou superior.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ComputerName
)
Write-Output $ComputerName
}
Agora que o ComputerName é necessário, se um não for especificado, a função solicitará um.
Test-MrParameterValidation
cmdlet Test-MrParameterValidation at command pipeline position 1
Supply values for the following parameters:
ComputerName:
Se desejar permitir mais de um valor para o parâmetro ComputerName, use o String datatype, mas adicione colchetes ([]
) ao datatype para permitir uma matriz de strings.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string[]]$ComputerName
)
Write-Output $ComputerName
}
Talvez você queira especificar um valor padrão para o parâmetro ComputerName se um não for especificado.
O problema é que os valores padrão não podem ser usados com parâmetros obrigatórios. Em vez disso, use o atributo de validação de parâmetro ValidateNotNullOrEmpty
com um valor padrão.
Mesmo ao definir um valor padrão, tente não usar valores estáticos. No exemplo a seguir, $env:COMPUTERNAME
é usado como o valor padrão, que é convertido automaticamente para o nome do computador local se um valor não for fornecido.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Write-Output $ComputerName
}
Saída detalhada
Os comentários embutidos são úteis se você estiver escrevendo um código complexo, mas os usuários não os verão a menos que olhem para o código.
A função no exemplo a seguir tem um comentário embutido no loop foreach
. Embora este comentário em particular possa não ser difícil de localizar, imagine se a função contivesse centenas de linhas de código.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
#Attempting to perform an action on $Computer <<-- Don't use
#inline comments like this, use write verbose instead.
Write-Output $Computer
}
}
Uma opção melhor é usar Write-Verbose
em vez de comentários em linha.
function Test-MrVerboseOutput {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
foreach ($Computer in $ComputerName) {
Write-Verbose -Message "Attempting to perform an action on $Computer"
Write-Output $Computer
}
}
A saída detalhada não é exibida quando a função é chamada sem o parâmetro Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02
A saída detalhada é mostrada quando a função é chamada com o parâmetro Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02 -Verbose
Entrada de canalização
Um código extra é necessário quando você deseja que sua função aceite a entrada do pipeline. Como mencionado anteriormente neste livro, os comandos podem aceitar a entrada de pipeline por valor (por tipo) ou por nome de propriedade. Você pode escrever suas funções como os comandos nativos para que eles aceitem um ou ambos os tipos de entrada.
Para aceitar a entrada do pipeline por valor, especifique o atributo de parâmetro ValueFromPipeline
para esse parâmetro. Você só pode aceitar a entrada de pipeline por valor a partir de um parâmetro de cada tipo de dados. Se tiveres dois parâmetros que aceitam entrada de cadeia de caracteres, apenas um deles pode aceitar entrada do pipeline por valor. Se você especificou por valor para ambos os parâmetros de cadeia de caracteres, a entrada não saberia a qual parâmetro se ligar. Esse cenário é outra razão pela qual chamo esse tipo de entrada de pipeline de por tipo em vez de por valor.
A entrada do pipeline é recebida um item de cada vez, tal como os itens são processados num loop de foreach
.
Um bloco process
é necessário para processar cada item se sua função aceitar uma matriz como entrada. Se sua função só aceita um único valor como entrada, um bloco process
não é necessário, mas é recomendado para consistência.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
Aceitar entrada de pipeline pelo nome da propriedade é semelhante, exceto que o especifica com o atributo de parâmetro ValueFromPipelineByPropertyName
, e pode ser especificado para qualquer número de parâmetros, independentemente do tipo de dados. A chave é a saída do comando que está sendo canalizado, deve ter um nome de propriedade que corresponda ao nome do parâmetro ou um alias de parâmetro da sua função.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
Os blocos begin
e end
são opcionais.
begin
é especificado antes do bloco process
e é usado para executar qualquer trabalho inicial antes que os itens sejam recebidos do pipeline. Os valores encaminhados não estão acessíveis no bloco begin
. O bloco end
é especificado após o bloco process
e é usado para limpeza depois que todos os itens canalizados são processados.
Tratamento de erros
A função mostrada no exemplo a seguir gera uma exceção sem tratamento quando um computador não pode ser contatado.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
Test-WSMan -ComputerName $Computer
}
}
}
Há algumas maneiras diferentes de lidar com erros no PowerShell.
Try/Catch
é a forma mais moderna de lidar com erros.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Embora a função mostrada no exemplo anterior use manipulação de erros, ela gera uma exceção não tratada porque o comando não gera um erro de encerramento. Apenas os erros terminais são detetados. Especifique o parâmetro ErrorAction com Stop como seu valor para transformar um erro não terminativo em um erro de encerramento.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
try {
Test-WSMan -ComputerName $Computer -ErrorAction Stop
}
catch {
Write-Warning -Message "Unable to connect to Computer: $Computer"
}
}
}
}
Não modifique a variável $ErrorActionPreference
global, a menos que seja absolutamente necessário. Se tu o alterares num escopo local, reverte para o valor anterior quando saíres desse escopo.
Se você estiver usando algo como .NET diretamente de sua função do PowerShell, não poderá especificar o parâmetro ErrorAction no próprio comando. Você pode alterar a variável $ErrorActionPreference
pouco antes de chamar o método .NET.
Ajuda baseada em comentários
Adicionar ajuda às suas funções é considerado uma prática recomendada. A ajuda permite que as pessoas com quem partilhas saibam como as usar.
function Get-MrAutoStoppedService {
<#
.SYNOPSIS
Returns a list of services that are set to start automatically, are not
currently running, excluding the services that are set to delayed start.
.DESCRIPTION
Get-MrAutoStoppedService is a function that returns a list of services
from the specified remote computer(s) that are set to start
automatically, are not currently running, and it excludes the services
that are set to start automatically with a delayed startup.
.PARAMETER ComputerName
The remote computer(s) to check the status of the services on.
.PARAMETER Credential
Specifies a user account that has permission to perform this action. The
default is the current user.
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1', 'Server2'
.EXAMPLE
'Server1', 'Server2' | Get-MrAutoStoppedService
.EXAMPLE
Get-MrAutoStoppedService -ComputerName 'Server1' -Credential (Get-Credential)
.INPUTS
String
.OUTPUTS
PSCustomObject
.NOTES
Author: Mike F. Robbins
Website: https://mikefrobbins.com
Twitter: @mikefrobbins
#>
[CmdletBinding()]
param (
)
#Function Body
}
Ao adicionar ajuda baseada em comentários às suas funções, a ajuda pode ser recuperada para elas como acontece com os comandos internos padrão.
Toda a sintaxe para escrever uma função no PowerShell pode parecer esmagadora para alguém que está começando. Se você não se lembrar da sintaxe de algo, abra uma segunda instância do ISE (Ambiente de Script Integrado) do PowerShell em um monitor separado e exiba o trecho "Cmdlet (função avançada) - Concluído" enquanto digita o código para suas funções. Trechos podem ser acessados no ISE do PowerShell usando a combinação de teclas Ctrl + J.
Resumo
Neste capítulo, você aprendeu as noções básicas de escrever funções no PowerShell, incluindo como:
- Crie funções avançadas
- Usar validação de parâmetros
- Usar saída detalhada
- Suporte para entrada de pipeline
- Lidar com erros
- Criar ajuda baseada em comentários
Avaliação
- Como obter uma lista de verbos aprovados no PowerShell?
- Como transformar uma função do PowerShell em uma função avançada?
- Quando os parâmetros WhatIf e Confirm devem ser adicionados às suas funções do PowerShell?
- Como transformas um erro não terminável em um erro terminável?
- Por que você deve adicionar ajuda baseada em comentários às suas funções?