Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
One-liner di PowerShell e script che devono essere modificati spesso sono buoni candidati per essere trasformati in funzioni riutilizzabili.
Scrivere funzioni quando possibile perché sono più orientate agli strumenti. È possibile aggiungere le funzioni a un modulo script, inserire tale modulo in una posizione definita nel $env:PSModulePathe chiamare le funzioni senza dover individuare dove sono state salvate le funzioni. Usando il modulo
Non sovraplicare le cose. Mantenere semplice e usare il modo più semplice per eseguire un'attività. Evitare alias e parametri posizionali in qualsiasi codice riutilizzato. Formattare il codice per la leggibilità. Non codificare i valori in modo fisso; utilizza parametri e variabili. Non scrivere codice non necessario anche se non fa male a nulla. Aggiunge complessità non necessarie. Prestare attenzione ai dettagli è fondamentale quando si scrive del codice PowerShell.
Denominazione
Quando si assegnano nomi alle funzioni in PowerShell, usare un caso Pascal nome con un verbo approvato e un sostantivo singolare. Per ottenere un elenco di verbi approvati in PowerShell, eseguire Get-Verb. Nell'esempio seguente, i risultati di Get-Verb sono ordinati in base alla proprietà Verb.
Get-Verb | Sort-Object -Property Verb
La proprietà Group offre un'idea del modo in cui usare i verbi.
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 usare un verbo approvato per le funzioni di PowerShell. I moduli che contengono funzioni con verbi non approvati generano un messaggio di avviso quando vengono importati in una sessione di PowerShell. Questo messaggio di avviso fa apparire le tue funzioni poco professionali. Anche i verbi non approvati limitano l'individuabilità delle funzioni.
Una funzione semplice
Una funzione in PowerShell viene dichiarata con la parola chiave function seguita dal nome della funzione e quindi da una parentesi graffa di apertura e chiusura ({ }). Il codice eseguito dalla funzione è contenuto all'interno di tali parentesi graffe.
function Get-Version {
$PSVersionTable.PSVersion
}
La funzione illustrata nell'esempio seguente è un semplice esempio che restituisce la versione di PowerShell.
Get-Version
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Quando si usa un nome generico per le funzioni, ad esempio Get-Version, potrebbe causare conflitti di denominazione. I comandi predefiniti aggiunti in futuro o i comandi che altri potrebbero scrivere potrebbero entrare in conflitto con essi. Anteporre la parte sostantiva dei nomi delle funzioni per evitare conflitti di denominazione. Ad esempio: <ApprovedVerb>-<Prefix><SingularNoun>.
Nell'esempio seguente viene usato il prefisso PS.
function Get-PSVersion {
$PSVersionTable.PSVersion
}
Oltre al nome, questa funzione è identica a quella precedente.
Get-PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
È comunque possibile avere un conflitto di nomi anche quando si aggiunge un prefisso al sostantivo. Mi piace anteporre i miei sostantivi di funzione con le mie iniziali. Sviluppare uno standard e attenersi a esso.
function Get-MrPSVersion {
$PSVersionTable.PSVersion
}
Questa funzione non è diversa dai due precedenti, ad eccezione dell'uso di un nome più univoco per tentare di evitare conflitti di denominazione con altri comandi di PowerShell.
Get-MrPSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 14393 693
Dopo il caricamento in memoria, è possibile visualizzare le funzioni nella funzione PSDrive.
Get-ChildItem -Path Function:\Get-*Version
CommandType Name Version
----------- ---- -------
Function Get-Version
Function Get-PSVersion
Function Get-MrPSVersion
Per rimuovere queste funzioni dalla sessione corrente, rimuoverle dalla funzione PSDrive o chiudere e riaprire PowerShell.
Get-ChildItem -Path Function:\Get-*Version | Remove-Item
Verificare che le funzioni siano state effettivamente rimosse.
Get-ChildItem -Path Function:\Get-*Version
Se le funzioni sono state caricate come parte di un modulo, è possibile scaricare il modulo per rimuoverle.
Remove-Module -Name <ModuleName>
Il cmdlet Remove-Module rimuove i moduli di PowerShell dalla memoria nella sessione di PowerShell corrente. Non li rimuove dal sistema o dal disco.
Parametri
Non assegnare valori in modo statico. Usare invece parametri e variabili. Quando si assegnano nomi ai parametri, usare lo stesso nome dei cmdlet predefiniti per i nomi dei parametri, quando possibile.
Nella funzione seguente si noti che è stato usato ComputerName e non Computer, ServerNameo Host per il nome del parametro. L'uso di ComputerName standardizza il nome del parametro in modo che corrisponda in nome e maiuscole/minuscole a quello dei cmdlet predefiniti.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
La funzione seguente esegue una query su tutti i comandi nel sistema e restituisce il numero con nomi di parametri specifici.
function Get-MrParameterCount {
param (
[string[]]$ParameterName
)
foreach ($Parameter in $ParameterName) {
$Results = Get-Command -ParameterName $Parameter -ErrorAction SilentlyContinue
[pscustomobject]@{
ParameterName = $Parameter
NumberOfCmdlets = $Results.Count
}
}
}
Come si può notare nei risultati seguenti, 39 comandi con un parametro ComputerName. Non sono disponibili comandi con parametri quali Computer, ServerName, Hosto Machine.
Get-MrParameterCount -ParameterName ComputerName, Computer, ServerName,
Host, Machine
ParameterName NumberOfCmdlets
------------- ---------------
ComputerName 39
Computer 0
ServerName 0
Host 0
Machine 0
Usare lo stesso formato maiuscolo/minuscolo per i nomi dei parametri come nei cmdlet predefiniti. Ad esempio, usare ComputerName, non computername. Questo schema di denominazione consente agli utenti che hanno familiarità con PowerShell di individuare le funzioni e di avere un aspetto simile ai cmdlet predefiniti.
L'istruzione param consente di definire uno o più parametri. Una virgola (,) separa le definizioni dei parametri. Per altre informazioni, vedere about_Functions_Advanced_Parameters.
Funzioni avanzate
Trasformare una funzione in una funzione avanzata in PowerShell è semplice. Una delle differenze tra una funzione e una funzione avanzata è che le funzioni avanzate hanno parametri comuni che vengono aggiunti automaticamente. I parametri comuni includono parametri quali Verboso e Debug.
Iniziare con la funzione Test-MrParameter usata nella sezione precedente.
function Test-MrParameter {
param (
$ComputerName
)
Write-Output $ComputerName
}
Esistono due modi diversi per visualizzare i parametri comuni. Uno consiste nel visualizzare la sintassi con Get-Command.
Get-Command -Name Test-MrParameter -Syntax
Si noti che la funzione Test-MrParameter non ha parametri comuni.
Test-MrParameter [[-ComputerName] <Object>]
Un altro consiste nell'approfondire la proprietà parameters di Get-Command.
(Get-Command -Name Test-MrParameter).Parameters.Keys
ComputerName
Aggiungere l'attributo CmdletBinding per trasformare la funzione in una funzione avanzata.
function Test-MrCmdletBinding {
[CmdletBinding()] # Turns a regular function into an advanced function
param (
$ComputerName
)
Write-Output $ComputerName
}
Quando si specifica CmdletBinding, i parametri comuni vengono aggiunti automaticamente.
CmdletBinding richiede un blocco param, ma il blocco param può essere vuoto.
Get-Command -Name Test-MrCmdletBinding -Syntax
Test-MrCmdletBinding [[-ComputerName] <Object>] [<CommonParameters>]
Il drill-down nella proprietà dei parametri di Get-Command mostra i nomi effettivi dei parametri, inclusi quelli comuni.
(Get-Command -Name Test-MrCmdletBinding).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
SupportsShouldProcess
L'attributo SupportsShouldProcess aggiunge i parametri di mitigazione dei rischi What If e Confirm. Questi parametri sono necessari solo per i comandi che apportano modifiche.
function Test-MrSupportsShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param (
$ComputerName
)
Write-Output $ComputerName
}
Si noti che ora ci sono i parametri WhatIf e Confirm.
Get-Command -Name Test-MrSupportsShouldProcess -Syntax
Test-MrSupportsShouldProcess [[-ComputerName] <Object>] [-WhatIf] [-Confirm]
[<CommonParameters>]
Anche in questo caso, è possibile usare Get-Command per restituire un elenco dei nomi effettivi dei parametri, inclusi quelli comuni, insieme a WhatIf e Confirm.
(Get-Command -Name Test-MrSupportsShouldProcess).Parameters.Keys
ComputerName
Verbose
Debug
ErrorAction
WarningAction
InformationAction
ErrorVariable
WarningVariable
InformationVariable
OutVariable
OutBuffer
PipelineVariable
WhatIf
Confirm
Convalida dei parametri
Convalidare l'input in anticipo. Non consentire al codice di continuare in un percorso quando non può essere completato senza input valido.
Specificare sempre un tipo di dati per le variabili usate per i parametri. Nell'esempio seguente, Stringa viene specificato come tipo di dati per il parametro ComputerName. Questa convalida limita a consentire solo la specifica di un singolo nome di computer per il parametro NomeComputer.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[string]$ComputerName
)
Write-Output $ComputerName
}
Se si specificano più nomi di computer, viene generato un errore.
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
Il problema con la definizione corrente è che è valido omettere il valore del parametro ComputerName, ma per il completamento della funzione è necessario un valore. Questo scenario è il caso in cui l'attributo del parametro Mandatory sia utile.
La sintassi usata nell'esempio seguente è compatibile con PowerShell versione 3.0 e successive.
[Parameter(Mandatory=$true)] si può specificare per rendere la funzione compatibile con la versione 2.0 di PowerShell o successiva.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$ComputerName
)
Write-Output $ComputerName
}
Ora che è necessario il nome del computer , se uno non è specificato, la funzione ne richiede uno.
Test-MrParameterValidation
cmdlet Test-MrParameterValidation at command pipeline position 1
Supply values for the following parameters:
ComputerName:
Se si desidera consentire più valori per il parametro
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string[]]$ComputerName
)
Write-Output $ComputerName
}
Forse vuoi specificare un valore predefinito per il parametro ComputerName se non è specificato.
Il problema è che i valori predefiniti non possono essere usati con parametri obbligatori. Usare invece l'attributo di convalida dei parametri ValidateNotNullOrEmpty con un valore predefinito.
Anche quando si imposta un valore predefinito, provare a non usare valori statici. Nell'esempio seguente, $env:COMPUTERNAME viene usato come valore predefinito, che viene convertito automaticamente nel nome del computer locale se non viene specificato un valore.
function Test-MrParameterValidation {
[CmdletBinding()]
param (
[ValidateNotNullOrEmpty()]
[string[]]$ComputerName = $env:COMPUTERNAME
)
Write-Output $ComputerName
}
Output verboso
I commenti inline sono utili se si scrive codice complesso, ma gli utenti non li vedono a meno che non esaminino il codice.
La funzione nell'esempio seguente ha un commento inline nel ciclo foreach. Anche se questo particolare commento potrebbe non essere difficile da individuare, immaginare se la funzione conteneva centinaia di righe di codice.
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
}
}
Un'opzione migliore consiste nell'usare Write-Verbose anziché commenti inline.
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
}
}
L'output verbose non viene visualizzato quando la funzione viene chiamata senza il parametro Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02
L'output dettagliato viene visualizzato quando la funzione viene chiamata con il parametro Verbose.
Test-MrVerboseOutput -ComputerName Server01, Server02 -Verbose
Input della pipeline
Quando si vuole che la funzione accetti l'input della pipeline, è necessario codice aggiuntivo. Come accennato in precedenza in questo libro, i comandi possono accettare input della pipeline per valore per tipo o in base al nome della proprietà. È possibile scrivere funzioni come i comandi nativi in modo che accettino uno o entrambi questi tipi di input.
Per accettare input della pipeline per valore, specificare l'attributo ValueFromPipeline per quel particolare parametro. Si può accettare l'input della pipeline solo per valore da un parametro per ciascun tipo di dati. Se si dispone di due parametri che accettano un input di tipo stringa, solo uno di essi può accettare l'input della pipeline per valore. Se specifichi per valore per entrambi i parametri stringa, l'input non saprebbe a quale parametro associarsi. Questo scenario è un altro motivo per cui io chiamo questo tipo di input della pipeline in base al tipo anziché in base al valore.
L'input della pipeline viene ricevuto un elemento alla volta, in modo analogo al modo in cui gli elementi vengono gestiti in un ciclo di foreach.
È necessario un blocco process per elaborare ogni elemento se la funzione accetta una matrice come input. Se la funzione accetta solo un singolo valore come input, non è necessario un blocco process ma consigliato per la coerenza.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
L'accettazione dell'input della pipeline in base al nome della proprietà è simile, tranne che viene specificata con l'attributo del parametro ValueFromPipelineByPropertyName e può essere applicata a qualsiasi numero di parametri, indipendentemente dal tipo di dati. La chiave è che l'output del comando passato tramite pipe deve avere un nome di proprietà corrispondente al nome del parametro o a un alias del parametro della tua funzione.
function Test-MrPipelineInput {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
Write-Output $ComputerName
}
}
i blocchi begin e end sono facoltativi.
begin viene specificato prima del blocco process e viene usato per eseguire qualsiasi operazione iniziale prima che gli elementi vengano ricevuti dalla pipeline. I valori inviati tramite pipe in non sono accessibili nel blocco begin. Il blocco end viene specificato dopo il blocco process e viene utilizzato per la pulizia dopo l'elaborazione di tutti gli elementi inviati tramite pipe in.
Gestione degli errori
La funzione illustrata nell'esempio seguente genera un'eccezione non gestita quando non è possibile contattare un computer.
function Test-MrErrorHandling {
[CmdletBinding()]
param (
[Parameter(Mandatory,
ValueFromPipeline,
ValueFromPipelineByPropertyName)]
[string[]]$ComputerName
)
process {
foreach ($Computer in $ComputerName) {
Test-WSMan -ComputerName $Computer
}
}
}
Esistono due modi diversi per gestire gli errori in PowerShell.
Try/Catch è il modo più moderno per gestire gli errori.
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"
}
}
}
}
Anche se la funzione illustrata nell'esempio precedente usa la gestione degli errori, genera un'eccezione non gestita perché il comando non genera un errore irreversibile. Vengono rilevati solo errori che provocano la terminazione. Specificare il parametro ErrorAction con Stop come valore per trasformare un errore non terminante in uno terminante.
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"
}
}
}
}
Non modificare la variabile $ErrorActionPreference globale a meno che non sia assolutamente necessario. Se lo si modifica in un ambito locale, viene ripristinato il valore precedente quando si esce da tale ambito.
Se si usa qualcosa di simile a .NET direttamente dall'interno della funzione di PowerShell, non è possibile specificare il parametro ErrorAction $ErrorActionPreference subito prima di chiamare il metodo .NET.
Guida basata su commenti
L'aggiunta di guida alle funzioni è considerata una buona pratica. Le funzioni di aiuto consentono alle persone con cui vengono condivise di sapere come utilizzarle.
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
}
Quando aggiungi una documentazione basata su commenti alle tue funzioni, è possibile richiamare l'aiuto per esse, proprio come avviene per i comandi predefiniti.
La sintassi per scrivere una funzione in PowerShell può sembrare opprimente per chi inizia. Se non è possibile ricordare la sintassi per un elemento, aprire una seconda istanza di PowerShell Integrated Scripting Environment (ISE) in un monitor separato e visualizzare il frammento "Cmdlet (funzione avanzata) - Complete" durante la digitazione nel codice per le funzioni. È possibile accedere ai frammenti di codice in PowerShell ISE usando la combinazione di tasti CTRL + J.
Sommario
In questo capitolo sono state illustrate le nozioni di base sulla scrittura di funzioni in PowerShell, tra cui le procedure seguenti:
- Creare funzioni avanzate
- Usare la convalida dei parametri
- Usare l'output dettagliato
- Supporto per l'input della pipeline
- Gestire gli errori
- Creare un aiuto basato su commenti
Recensione
- Come si ottiene un elenco di verbi approvati in PowerShell?
- Come si trasforma una funzione di PowerShell in una funzione avanzata?
- Quando si dovrebbero aggiungere i parametri WhatIf e Confirm alle funzioni di PowerShell?
- Come si trasforma un errore non terminante in un errore terminante?
- Perché è consigliabile aggiungere una Guida basata su commenti alle funzioni?
Referenze
- about_Functions
- sui_Funzioni_Parametri_Avanzati
- informazioni su_CommonParameters
- informazioni_sulle_funzionalità_CmdletBindingAttribute
- about_Functions_Advanced
- about_Try_Catch_Finally
- informazioni_sull'aiuto_basato_su_commenti
- Video: Creazione di strumenti PowerShell con Funzioni Avanzate e Moduli Script
Passaggi successivi
Nel capitolo 10 si apprenderà come creare pacchetti di funzioni nei moduli di script. Si esamineranno la struttura dei moduli, i manifesti, l'esportazione di comandi pubblici e le procedure consigliate per organizzare, condividere e gestire gli strumenti di PowerShell riutilizzabili.