Parametri di PowerShell: Sblocca il Potere dello Scripting

Tutti i comandi PowerShell possono avere uno o più parametri, a volte chiamati argomenti. Se non stai utilizzando i parametri PowerShell nelle tue funzioni PowerShell, non stai scrivendo un buon codice PowerShell!

In questo articolo, imparerai praticamente ogni aspetto della creazione e dell’uso dei parametri o degli argomenti di PowerShell!

Questo è un esempio tratto dal mio libro PowerShell for SysAdmins. Se vuoi imparare PowerShell o imparare alcuni trucchi del mestiere, dagli un’occhiata!

Perché hai bisogno di un parametro?

Quando inizi a creare funzioni, avrai l’opzione di includere parametri o meno e come funzionano quei parametri.

Supponiamo tu abbia una funzione che installa Microsoft Office. Forse chiama l’installatore di Office in modo silenzioso all’interno della funzione. Ciò che fa la funzione non importa per i nostri scopi. La funzione di base ha questo aspetto con il nome della funzione e il blocco di script.

function Install-Office {
    ## esegui qui l'installazione silenziosa
}

In questo esempio, hai appena eseguito Install-Office senza parametri e fa ciò che deve fare.

Non importava se la funzione Install-Office aveva parametri o meno. Apparentemente non aveva parametri obbligatori; altrimenti, PowerShell non ci avrebbe permesso di eseguirlo senza utilizzare un parametro.

Quando usare un parametro PowerShell

Office ha molte versioni diverse. Forse è necessario installare Office 2013 e 2016. Attualmente, non hai modo di specificare questo. Potresti cambiare il codice della funzione ogni volta che desideri modificare il comportamento.

Ad esempio, potresti creare due funzioni separate per installare versioni diverse.

function Install-Office2013 {
    Write-Host 'I installed Office 2013. Yippee!'
}

function Install-Office2016 {
    Write-Host 'I installed Office 2016. Yippee!'
}

Fare questo funziona, ma non è scalabile. Ti costringe a creare una funzione separata per ogni singola versione di Office che esce. Sarai costretto a duplicare molto codice quando non è necessario.

Invece, hai bisogno di un modo per passare valori diversi al momento dell’esecuzione per modificare il comportamento della funzione. Come si fa questo?

Sì! Parametri o quello che alcuni chiamano argomenti.

Dato che vorremmo installare diverse versioni di Office senza cambiare il codice ogni volta, devi aggiungere almeno un parametro a questa funzione.

Prima di pensare rapidamente a un parametro di PowerShell da usare, è essenziale chiedersi prima di tutto una domanda; “Qual è il cambiamento o i cambiamenti più piccoli che ti aspetti saranno necessari in questa funzione?”.

Ricorda che devi rieseguire questa funzione senza cambiare alcun codice all’interno della funzione stessa. In questo esempio, il parametro probabilmente ti è chiaro; devi aggiungere un parametro Versione. Ma, quando hai una funzione con decine di righe di codice, la risposta non sarà troppo evidente. Finché rispondi a quella domanda nel modo più preciso possibile, ti aiuterà sempre.

Quindi sai di aver bisogno di un parametro Versione. Ora cosa? Ora puoi aggiungerne uno, ma come qualsiasi grande linguaggio di programmazione, ci sono più modi per farlo.

In questo tutorial, ti mostrerò il modo “migliore” per creare parametri basato sulla mia esperienza di quasi un decennio con PowerShell. Tuttavia, sappi che questo non è l’unico modo per creare un parametro.

Esiste una cosa chiamata parametri posizionali. Questi parametri ti permettono di passare valori ai parametri senza specificare il nome del parametro. I parametri posizionali funzionano ma non sono considerati “la pratica migliore”. Perché? Perché sono più difficili da leggere, specialmente quando hai molti parametri definiti su una funzione.

Creare un Semplice Parametro PowerShell

Creare un parametro su una funzione richiede due componenti principali; un blocco param e il parametro stesso. Un blocco param è definito dalla parola chiave param seguita da un set di parentesi.

An example of a param block
function Install-Office { [CmdletBinding()] param() Write-Host 'I installed Office 2016. Yippee!' }

A questo punto, la funzionalità effettiva della funzione non è cambiata affatto. Abbiamo solo messo insieme un po’ di tubature, preparandoci per il primo parametro.

Una volta che abbiamo il blocco param in posizione, ora creerai il parametro. Il metodo che sto suggerendo per creare un parametro include il blocco Parameter, seguito dal tipo di parametro, seguito dal nome della variabile parametro qui sotto.

An example of a param block
function Install-Office { [CmdletBinding()] param( [Parameter()] [string]$Version ) Write-Host 'I installed Office 2016. Yippee!' }

Abbiamo ora creato un parametro di funzione in PowerShell ma cosa è esattamente successo qui?

Il blocco Parameter è un pezzo opzionale ma consigliato di ogni parametro. Come il blocco param, è una “tubatura di funzione”, che prepara il parametro per l’aggiunta di ulteriori funzionalità. La seconda riga è dove definisci il tipo di parametro che è.

In questo caso, abbiamo scelto di convertire il parametro Version in una stringa. Definire esplicitamente il tipo significa che qualsiasi valore passato a questo parametro verrà sempre “convertito” in una stringa se non lo è già.

Il tipo non è obbligatorio, ma è altamente consigliato. Definire esplicitamente il tipo del parametro ridurrà significativamente molte situazioni indesiderate in futuro. Fidati di me.

Ora che hai definito il parametro, puoi eseguire il comando Install-Office con il parametro Version passando una stringa di versione come 2013. Il valore passato al parametro Version viene talvolta indicato come argomenti o valori del parametro.

Passing a Parameter to the Function
PS> Install-Office -Version 2013 I installed Office 2016. Yippee!

Cosa sta succedendo qui comunque? Hai detto che volevi installare la versione 2013, ma ti sta ancora dicendo che ha installato la versione 2016. Quando aggiungi un parametro, devi poi ricordarti di cambiare il codice della funzione in una variabile. Quando il parametro viene passato alla funzione, quella variabile verrà espansa per essere qualsiasi valore sia stato passato.

Cambia il testo statico di 2016 e sostituiscilo con la variabile del parametro Version e converti i singoli apici in doppi apici in modo che la variabile si espanda.

Modifying function code account for a parameter
function Install-Office { [CmdletBinding()] param( [Parameter()] [string]$Version ) Write-Host "I installed Office $Version. Yippee!" }

Ora puoi vedere che qualsiasi valore passi al parametro Version verrà poi passato nella funzione come variabile $Version.

L’attributo del parametro obbligatorio

Ricorda che ho menzionato che la riga [Parameter ()] era solo “plumbing della funzione” e doveva preparare la funzione per lavori successivi? Aggiungere attributi di parametro a un parametro è il lavoro aggiuntivo di cui parlavo in precedenza.

A parameter doesn’t have to be a placeholder for a variable. PowerShell has a concept called parameter attributes and parameter validation. Parameter attributes change the behavior of the parameter in a lot of different ways.

Ad esempio, uno degli attributi di parametro più comuni che imposterai è la parola chiave Mandatory. Per impostazione predefinita, potresti chiamare la funzione Install-Office senza utilizzare il parametro Version e funzionerebbe correttamente. Il parametro Version sarebbe opzionale. Ammesso che non espanderebbe la variabile $Version all’interno della funzione perché non c’era alcun valore, ma comunque si eseguirebbe.

Spesso, quando si crea un parametro, si desidera che l’utente utilizzi sempre quel parametro. Ti affiderai al valore del parametro all’interno del codice della funzione da qualche parte e se il parametro non viene passato, la funzione avrà esito negativo. In questi casi, si desidera obbligare l’utente a passare questo valore di parametro alla tua funzione. Vuoi che quel parametro diventi obbligatorio.

Obbligare gli utenti a utilizzare un parametro è semplice una volta che hai costruito il framework di base come hai fatto qui. Devi includere la parola chiave Mandatory all’interno delle parentesi del parametro. Una volta che hai fatto questo, l’esecuzione della funzione senza il parametro si interromperà fino a quando non verrà inserito un valore.

Using a mandatory parameter
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Write-Host "I installed Office $Version. Yippee!" } PS> Install-Office cmdlet Install-Office at command pipeline position 1 Supply values for the following parameters: Version:

La funzione attende finché non specifici un valore per il parametro Version. Una volta fatto ciò e premi Invio, PowerShell eseguirà la funzione e passerà oltre. Se fornisci un valore per il parametro, PowerShell non ti richiederà il parametro ogni volta.

Attributi di Validazione dei Parametri di PowerShell

Rendere un parametro obbligatorio è uno degli attributi di parametro più comuni che è possibile aggiungere, ma è anche possibile utilizzare attributi di validazione del parametro. In programmazione, è sempre essenziale limitare l’input dell’utente il più possibile. Limitare le informazioni che gli utenti (o anche tu!) possono passare alle tue funzioni o script eliminerà codice superfluo all’interno della tua funzione che deve tener conto di tutte le situazioni.

Apprendimento tramite Esempio

Ad esempio, nella funzione Install-Office, ho dimostrato di passare il valore 2013 perché sapevo che avrebbe funzionato. Ho scritto il codice! Sto supponendo (mai farlo nel codice!) che sia ovvio che chiunque sappia qualcosa specifica la versione come 2013 o 2016. Beh, ciò che è ovvio per te potrebbe non esserlo altrettanto per altre persone.

Se vuoi diventare tecnico sulle versioni, sarebbe probabilmente più preciso specificare la versione 2013 come 15.0 e 2016 come 16.0 se Microsoft utilizzasse ancora lo schema di versionamento che ha utilizzato in passato. Ma cosa succede se, poiché stai assumendo che specificano una versione del 2013 o 2016, hai del codice all’interno della funzione che cerca cartelle con quelle versioni o qualcos’altro?

Ecco un esempio di dove potresti utilizzare la stringa $Version in un percorso del file. Se qualcuno passa un valore che non completa un nome cartella come Office2013 o Office2016, fallirà o farà qualcosa di peggio, come rimuovere cartelle non previste o cambiare cose che non hai considerato.

Assuming Parameter Values
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Version ) Get-ChildItem -Path "\\SRV1\Installers\Office$Version" } PS> Install-Office -Version '15.0' Get-ChildItem : Cannot find path '\SRV1\Installers\Office15.0' because it does not exist. At line:7 char:5 Get-ChildItem -Path "\\SRV1\Installers\Office$Version" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo : ObjectNotFound: (\SRV1\Installers\Office15.0:String) [Get-ChildItem], ItemNotFoundExcep tion FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

Per limitare l’utente a ciò che si aspetta di inserire, è possibile aggiungere una validazione del parametro PowerShell.

Utilizzo dell’attributo di validazione del parametro ValidateSet

Esistono vari tipi di validazione del parametro che è possibile utilizzare. Per un elenco completo, eseguire Get-Help about_Functions_Advanced_Parameters. In questo esempio, l’attributo ValidateSet sarebbe probabilmente il migliore.

L’attributo di validazione ValidateSet consente di specificare un elenco di valori consentiti come valore del parametro. Poiché stiamo considerando solo la stringa 2013 o 2016, desidero assicurarmi che l’utente possa specificare solo questi valori. In caso contrario, la funzione fallirà immediatamente, avvisandolo del motivo.

È possibile aggiungere attributi di validazione del parametro subito sotto la parola chiave Parameter originale. In questo esempio, all’interno delle parentesi dell’attributo del parametro, si ha un array di elementi; 2013 e 2016. Un attributo di validazione del parametro indica a PowerShell che i soli valori validi per Version sono 2013 o 2016. Se si prova a passare qualcosa diverso da quello presente nell’insieme, verrà visualizzato un errore che indica che sono disponibili solo un numero specifico di opzioni.

Using the ValidateSet parameter validation attribute
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('2013','2016')] [string]$Version ) Get-ChildItem -Path "\\SRV1\Installers\Office$Version" } PS> Install-Office -Version 15.0 Install-Office : Cannot validate argument on parameter 'Version'. The argument "15.0" does not belong to the set "2013,2016" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again. At line:1 char:25 Install-Office -Version 15.0 ~~~~ CategoryInfo : InvalidData: (:) [Install-Office], ParameterBindingValidationException FullyQualifiedErrorId : ParameterArgumentValidationError,Install-Office

L’attributo ValidateSet è un attributo di convalida comune da utilizzare. Per una panoramica completa di tutti i modi in cui i valori dei parametri possono essere limitati, consulta l’argomento di aiuto Functions_Advanced_Parameters eseguendo Get-Help about_Functions_Advanced_Parameters.

Set di parametri

Supponiamo che tu voglia solo che certi parametri di PowerShell siano utilizzati con altri parametri. Forse hai aggiunto un parametro Path alla funzione Install-Office. Questo percorso installerà qualsiasi versione sia disponibile. In questo caso, non vuoi che l’utente utilizzi il parametro Version.

Hai bisogno di set di parametri.

I parametri possono essere raggruppati in set che possono essere utilizzati solo con altri parametri nello stesso set. Utilizzando la funzione di seguito, ora puoi utilizzare sia il parametro Version che il parametro Path per generare il percorso per l’installatore.

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(Mandatory)]
        [string]$Path
    )
    
    if ($Version) {
        Get-ChildItem -Path "\\SRV1\Installers\Office$Version"
    } elseif ($Path) {
        Get-ChildItem -Path $Path
    }
}

Questo pone però un problema, perché l’utente potrebbe utilizzare entrambi i parametri. Inoltre, poiché entrambi i parametri sono obbligatori, saranno costretti a utilizzarli entrambi quando non è quello che desideri. Per risolvere questo problema, possiamo inserire ciascun parametro in un set di parametri come segue.

function Install-Office {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory,ParameterSetName = 'ByVersion')]
        [ValidateSet('2013','2016')]
        [string]$Version,
        
        [Parameter(Mandatory, ParameterSetName = 'ByPath')]
        [string]$Path
    )
    
    if ($Version) {
        Get-ChildItem -Path "\\SRV1\Installers\Office$Version"
    } elseif ($Path) {
        Get-ChildItem -Path $Path
    }
}

Definendo un nome per il set di parametri su ciascun parametro, ciò ti consente di controllare gruppi di parametri insieme.

Set di parametri predefinito

Cosa succede se l’utente cerca di eseguire Install-Office senza parametri? Questo non è previsto e verrà visualizzato un messaggio di errore amichevole.

No parameter set

Per risolvere questo, dovrai definire un insieme di parametri predefinito all’interno dell’area CmdletBinding(). Questo dice alla funzione di scegliere un insieme di parametri da utilizzare se non vengono utilizzati parametri espliciti, modificando [CmdletBinding()] in [CmdletBinding(DefaultParameterSetName = 'ByVersion')]

Ora, ogni volta che esegui Install-Office, chiederà il parametro Version poiché userà quel set di parametri.

Input del Pipeline

Nei precedenti esempi, hai creato funzioni con un parametro di PowerShell che può essere passato solo utilizzando la sintassi tipica -NomeParametro Valore. Ma, come hai già appreso, PowerShell ha un pipeline intuitiva che ti consente di passare senza soluzione di continuità oggetti da un comando a un altro senza utilizzare la sintassi “tipica”.

Quando usi il pipeline, “concateni” i comandi con il simbolo pipe | consentendo all’utente di inviare l’output di un comando come Get-Service a Start-Service come scorciatoia per passare il parametro Nome a Start-Service.

Il “Vecchio” Metodo Utilizzando un Ciclo

Nella funzione personalizzata con cui stai lavorando, stai installando Office e hai un parametro Versione. Diciamo che hai una lista di nomi di computer in un file CSV in una riga con la versione di Office che deve essere installata su di essi nella seconda riga. Il file CSV assomiglia a qualcosa del genere:

ComputerName,Version
PC1,2016
PC2,2013
PC3,2016

Vorresti installare la versione di Office che è accanto a ciascun computer su quel computer.

Innanzitutto, dovrai aggiungere un parametro ComputerName alla funzione per passare un nome computer diverso per ogni iterazione della funzione. Di seguito ho creato del pseudocodice che rappresenta parte del codice che potrebbe essere presente nella funzione fittizia e ho aggiunto un’istanza Write-Host per vedere come le variabili si espandono all’interno della funzione.

Adding the ComputerName parameter
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet('2013','2016')] [string]$Version, [Parameter()] [string]$ComputerName ) <# ## Connettiti al remoto con del codice qui Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Fai qualcosa per installare la versione di Office su questo computer Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version } #> Write-Host "I am installing Office version [$Version] on computer [$ComputerName]" }

Una volta che hai aggiunto il parametro ComputerName alla funzione; potresti far succedere ciò leggendo il file CSV e passando i valori del nome del computer e della versione alla funzione Install-Office.

$computers = Import-Csv -Path 'C:\ComputerOfficeVersions.csv'
foreach ($pc in $computers) {
    Install-Office -Version $_.Version -ComputerName $_.ComputerName
}

Creazione di Input per la Pipeline dei Parametri

Questo metodo di lettura delle righe del CSV e utilizzo di un loop per passare le proprietà di ogni riga alla funzione è il “vecchio” modo di farlo. In questa sezione, vuoi evitare completamente il ciclo foreach e utilizzare invece la pipeline.

Come è, la funzione non supporta affatto la pipeline. Avrebbe senso intuitivo presumere che potresti passare ogni nome computer e versione alla funzione utilizzando la pipeline. Di seguito, stiamo leggendo il CSV e passandolo direttamente a Install-Office, ma ciò non funziona.

No function pipeline input defined
PS> Import-Csv -Path 'C:\ComputerOfficeVersions.csv' | Install-Office

Puoi supporre tutto ciò che vuoi, ma questo non lo fa funzionare automaticamente. Ci viene richiesto il parametro Version quando sai che Import-Csv lo sta inviando come proprietà dell’oggetto. Perché non funziona? Perché non hai ancora aggiunto il supporto per il pipeline.

Esistono due tipi di input da pipeline in una funzione di PowerShell; ByValue (intero oggetto) e ByPropertyName (una singola proprietà dell’oggetto). Quale pensi sia il modo migliore per collegare l’output di Import-Csv all’input di Install-Office?

Senza alcun refactoring, potresti utilizzare il metodo ByPropertyName poiché, dopo tutto, Import-Csv sta già restituendo le proprietà Version e ComputerName poiché sono colonne nel CSV.

Aggiungere il supporto per il pipeline a una funzione personalizzata è molto più semplice di quanto possa sembrare. È semplicemente un attributo del parametro rappresentato con una delle due parole chiave; ValueFromPipeline o ValueFromPipelineByPropertyName.

Nell’esempio, desideri vincolare le proprietà ComputerName e Version restituite da Import-Csv ai parametri Version e ComputerName di Install-Office quindi utilizzerai ValueFromPipelineByPropertyName.

Dato che desideriamo vincolare entrambi questi parametri, aggiungerai questa parola chiave a entrambi i parametri come mostrato di seguito e rieseguirai la funzione utilizzando il pipeline.

Adding Pipeline Support
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet('2013','2016')] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) <# ## Connettersi al remoto con del codice qui Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Fare qualcosa per installare la versione di Office su questo computer Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version } #> Write-Host "I am installing Office version [$Version] on computer [$ComputerName]" }

È strano. È stato eseguito solo per l’ultima riga nel CSV. Cosa sta succedendo? Ha eseguito solo la funzione per l’ultima riga perché hai saltato un concetto che non è richiesto quando si costruiscono funzioni senza supporto per il pipeline.

Non dimenticare il blocco di processo!

Quando devi creare una funzione che coinvolge il supporto per il pipeline, devi includere (almeno) un blocco “integrato” all’interno della tua funzione chiamata. process. Questo blocco di processo dice a PowerShell che quando riceve input dal pipeline, esegua la funzione per ogni iterazione. Per impostazione predefinita, eseguirà solo l’ultima.

Potresti tecnicamente aggiungere altri blocchi come begin e end anche, ma gli scripter non li usano così spesso.

Per dire a PowerShell di eseguire questa funzione per ogni oggetto in ingresso, aggiungerò un blocco process che include il codice all’interno di quello.

Adding the process block
function Install-Office { [CmdletBinding()] param( [Parameter(Mandatory,ValueFromPipelineByPropertyName)] [ValidateSet('2013','2016')] [string]$Version, [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$ComputerName ) process { <# ## Connettersi al remoto con del codice qui Invoke-Command -ComputerName $ComputerName -ScriptBlock { ## Fare qualcosa per installare la versione di Office su questo computer Start-Process -FilePath 'msiexec.exe' -ArgumentList 'C:\Setup\Office{0}.msi' -f $using:Version } #> Write-Host "I am installing Office version [$Version] on computer [$ComputerName]" } }

Ora puoi vedere che le proprietà Version e ComputerName di ogni oggetto restituito da Import-Csv sono state passate a Install-Office e vincolate ai parametri Version e ComputerName.

Risorse

Per approfondire il funzionamento dei parametri delle funzioni, dai un’occhiata al mio post sul blog sulle funzioni di PowerShell.

I also encourage you to check my Pluralsight course entitled Building Advanced PowerShell Functions and Modules for an in-depth breakdown of everything there is to know about PowerShell functions, function parameters, and PowerShell modules.

Source:
https://adamtheautomator.com/powershell-parameter/