Articles

ForEach e Where magic methods

ForEach e Where sono due concetti di uso frequente che sono stati disponibili in PowerShell dalla versione 1 è uscito nel 2006. ForEach è disponibile sia come istruzione che come cmdlet (ForEach-Object), consentendo di scorrere una raccolta di oggetti e di eseguire alcune azioni una volta per ogni oggetto in quella raccolta. Where è stato disponibile come cmdlet (Where-Object), che consente di filtrare gli elementi in una raccolta che non passano alcune condizioni, una condizione che può essere valutata utilizzando le proprietà sugli oggetti nella raccolta o gli oggetti stessi che appartengono alla raccolta. La capacità ForEach di agire sugli elementi di una raccolta e la capacità Where di filtrare una raccolta sono caratteristiche molto utili e sono incluse in una forma o nell’altra nella logica alla base di molti script, comandi e moduli di PowerShell, indipendentemente dalla versione di PowerShell utilizzata. In realtà, sono così pesantemente utilizzati che sono stati un’area di messa a fuoco per il miglioramento delle prestazioni, funzionalità e sintassi nelle versioni PowerShell 3.0 e 4.0.

Con il rilascio di Windows PowerShell 4.0, sono stati introdotti due nuovi metodi “magici” per i tipi di raccolta che forniscono una nuova sintassi per l’accesso alle funzionalità ForEach e Where in Windows PowerShell. Questi metodi sono giustamente chiamati ForEach e Where. Chiamo questi metodi “magici” perché sono piuttosto magici nel modo in cui funzionano in PowerShell. Non vengono visualizzati nell’output Get-Member, anche se si applica-Force e request-MemberType All. Se ti rimbocchi le maniche e scavi con reflection, puoi trovarli; tuttavia, richiede una ricerca ampia perché sono metodi di estensione privati implementati su una classe privata. Eppure, anche se non sono rilevabili senza sbirciare sotto le coperte, sono lì quando ne hai bisogno, sono più veloci delle loro controparti più anziane e includono funzionalità che non erano disponibili nelle loro controparti più anziane, da qui la sensazione “magica” che ti lasciano quando li usi in PowerShell. Sfortunatamente, questi metodi rimangono privi di documenti anche oggi, quasi un anno da quando sono stati rilasciati pubblicamente, così tante persone non si rendono conto del potere che è disponibile in questi metodi. Questo articolo cercherà di correggerlo spiegando dove possono essere utilizzati e come funzionano in modo da poter sfruttare questa magia quando si utilizza PowerShell.

Una nota su PowerShell 3.0

Prima di iniziare a spiegare come funzionano i ForEach e Dove funzionano i metodi, ho bisogno di menzionare qualcosa rispetto a questi due metodi e PowerShell 3.0. Mentre è vero che i metodi ForEach e Where sono stati resi disponibili solo in PowerShell 4.0 e versioni successive, PowerShell 3.0 è ancora molto utilizzato in molti ambienti e, a meno che non si utilizzi PowerShell in un ambiente standardizzato su PowerShell 4.0 e versioni successive, è possibile che si desideri sfruttare la sintassi fornita dai nuovi metodi quando si utilizza PowerShell 3.0. Ho sentito che questo era un limite la pena di affrontare, così come parte del TypePx modulo che ho recentemente pubblicato su GitHub e in PowerShell di raccolta di Risorse (aka il PowerShellGet repository pubblico, attualmente in anteprima limitata), ho incluso ForEach e Dove script metodi che sono funzionalmente equivalenti ai metodi introdotti in PowerShell 4.0, che permette di sfruttare la nuova sintassi e la funzionalità, anche se si sta utilizzando PowerShell 3.0. Ci sono alcune carenze in questa implementazione, che evidenzierò più avanti in questo articolo.

Il metodo ForEach

ForEach è un metodo che consente di scorrere rapidamente una raccolta di oggetti e di agire su ciascun oggetto in quella raccolta. Questo metodo fornisce prestazioni più veloci rispetto alle sue controparti precedenti (l’istruzione foreach e il cmdlet ForEach-Object) e semplifica anche alcune delle azioni più comuni che potresti voler eseguire sugli oggetti nella raccolta. Tutti gli oggetti che vengono emessi da questo metodo vengono restituiti in una raccolta generica di tipo System.Raccolta.ObjectModel.Raccolta 1.

Ci sono sei modi supportati che è possibile richiamare questo metodo, e ciascuno di questi sarà spiegato in modo più dettagliato di seguito. Gli argomenti supportati che possono essere utilizzati quando si richiama il metodo ForEach sono i seguenti:

  • ForEach(scriptblock espressione)
  • ForEach(tipo convertToType)
  • ForEach(string propertyName)
  • ForEach(string propertyName, oggetto newValue)
  • ForEach(string methodName)
  • ForEach(string methodName, oggetto argomenti)
  • ForEach(scriptblock espressione, oggetto argomenti)

si noti che questi sono supportati argomento abbinamenti, non overload disponibili per il metodo ForEach. L’utilizzo di abbinamenti di argomenti diversi da questi può comportare errori che non identificano chiaramente quale sia il problema reale.

ForEach(scriptblock expression) e ForEach(scriptblock expression, object arguments)

Se si passa un’espressione di blocco di script nel metodo ForEach, si è in grado di eseguire lo stesso tipo di attività che si farebbe in un blocco di script che si utilizzerebbe con l’istruzione foreach o il cmdlet ForEach-Object. Inoltre, come il cmdlet ForEach-Object, le variabili PS _ e PS PSItem fanno riferimento all’elemento corrente che viene elaborato. Tutti gli argomenti forniti oltre all’argomento iniziale del blocco di script verranno utilizzati come argomenti per il blocco di script. Questo è proprio come il parametro-ArgumentList funziona sul cmdlet ForEach-Object. Ecco un esempio che dimostra come potresti usarlo per eseguire un blocco di script su ogni elemento di una raccolta:

# Get a set of services$services = Get-Service c*# Display the names and display names of all services in the collection$services.foreach{"$($_.Name) ($($_.DisplayName))"}# Select a property name to expand using a script block argument$services.foreach({param($PropertyName); $_.$PropertyName}, 'DisplayName')

Potresti aver notato qualcosa di strano in questa sintassi, perché non ho avvolto il blocco di script stesso tra parentesi. È possibile avvolgerlo tra parentesi tonde, tuttavia è facoltativo in PowerShell 4.0 o versioni successive perché il parser PowerShell è stato migliorato per consentire l’omissione delle parentesi ogni volta che si invoca un metodo che accetta un singolo argomento del blocco di script. Inoltre, come l’istruzione foreach e il cmdlet ForEach-Object, il blocco di script fornito viene richiamato nell’ambito corrente. Ciò significa che qualsiasi assegnazione di variabili effettuata all’interno di quel blocco di script persisterà dopo che il metodo ForEach ha terminato l’esecuzione.

ForEach(type convertToType)

Unico per il metodo ForEach, è possibile passare un tipo nel metodo ForEach se si desidera convertire ogni elemento in una raccolta in un altro tipo. Ad esempio, immagina di avere una raccolta di oggetti e di voler convertire tali oggetti nel loro equivalente stringa. Ecco cosa simile con il metodo ForEach:

# Get a collection of processes$processes = Get-Process# Convert the objects in that collection into their string equivalent$processes.foreach()

Si potrebbe avere eseguito la stessa operazione typecasting la raccolta in un array di tipo stringa (ad esempio ]$processi), e typecasting la matrice è infatti significativamente più veloce, ma c’è una buona possibilità che non sarebbe nemmeno notare la differenza di tempo di esecuzione a meno che non si sta lavorando con un molto, molto grande collezione. Nonostante la differenza di tempo, tenderò a preferire la sintassi del metodo ForEach in determinate situazioni se mi consente di mantenere l’eleganza nell’implementazione evitando parentesi tonde extra negli script che scrivo.

ForEach(string propertyName)

In PowerShell 3.0 e versioni successive, un secondo set di parametri è stato aggiunto a ForEach-Object per consentire di recuperare più facilmente il valore di una proprietà specifica semplicemente passando un nome di proprietà come unico valore di parametro per ForEach-Object. Questa convenienza è stata offerta nel metodo ForEach pure. Qui è un esempio che illustra come si può scorrere attraverso il recupero e la restituzione di un immobile di che collezione:

# Get all services whose name starts with "w"$services = Get-Service w*# Return the names of those services$services.foreach('Name')

naturalmente, a partire dalla versione 3.0 di PowerShell, si potrebbe semplicemente richiamare $servizi.Nome per ottenere i nomi di tutti i servizi, e che andranno a completare più veloce rispetto al metodo ForEach alternativa (anche se solo notare la differenza di prestazioni molto grandi raccolte nell’ordine di centinaia di migliaia di oggetti); tuttavia, funziona solo per proprietà che non si trovano nella raccolta stessa, ed è una sintassi con cui alcuni scripter non si sentono a proprio agio a causa della natura implicita di ciò che fa il comando. La nuova sintassi del metodo ForEach fornisce un’alternativa più esplicita che ha il vantaggio di essere anche un po ‘ più auto-documentante.

ForEach(string propertyName, object newValue)

Non solo è possibile recuperare una proprietà su una raccolta di oggetti, è possibile impostare una proprietà su una raccolta di oggetti pure. Questa è una funzionalità che non è disponibile negli altri foreach, a meno che non si crei esplicitamente il blocco di script per farlo. Per impostare la proprietà, si limita a fornire il nome della proprietà e il valore che si desidera utilizzare quando si imposta la proprietà, come:

# Note, this is not a realistic example# This would be used more commonly on configuration data$services = Get-Service c*# Now change the display names of every service to some new value$services.foreach('DisplayName','Hello')

Proprio come le assegnazioni si potrebbe fare utilizzando l’operatore uguale a, PowerShell tenterà di convertire qualunque cosa si fornisce come valore appropriato per il tipo di proprietà assegnato.

ForEach(string methodName) e ForEach(string methodName, object arguments)

Per invocare un metodo, è sufficiente fornire il nome del metodo come primo argomento e quindi gli argomenti per quel metodo come secondo, terzo, quarto, ecc. argomento. Se il metodo non accetta argomenti, puoi semplicemente passare il nome del metodo e verrà invocato senza argomenti. Ecco un esempio che mostra come si potrebbe uccidere un gruppo di processi che eseguono un programma specifico:

# Get all processes running Chrome$processes = Get-Process -Name Chrome# Now kill all of those processes$processes.foreach('Kill')

Ecco un altro esempio, questa volta utilizzando un metodo con argomenti mostrando in che modo si potrebbe verificare che i comandi sono seguendo le migliori pratiche attraverso l’utilizzo di opportuni nomi e alias parametri comunemente utilizzati:

# Get all commands that have a ComputerName parameter$cmds = Get-Command -ParameterName ComputerName# Now show a table making sure the parameter names and aliases are consistent$cmds.foreach('ResolveParameter','ComputerName') | Format-Table Name,Aliases

Come si può vedere da questi risultati, ci sono sicuramente alcune incongruenze nell’attuazione del ComputerName parametri che devono essere corretti.

Che copre tutte le funzionalità attualmente disponibili nel metodo ForEach. Come puoi vedere, non ci sono molte nuove funzionalità offerte in questo metodo, ma i miglioramenti della sintassi quando si esegue un’attività semplice su una raccolta di oggetti sono piacevoli, e i miglioramenti delle prestazioni del metodo ForEach rispetto all’istruzione foreach equivalente per la pipeline ForEach-Object sono sicuramente un miglioramento gradito. Con quella spiegazione fuori strada, passiamo al metodo Where.

Il metodo Where

Where è un metodo che consente di filtrare una raccolta di oggetti. Questo è molto simile al cmdlet Where-Object, ma il metodo Where è anche come Select-Object e Group-Object, include diverse funzionalità aggiuntive che il cmdlet Where-Object non supporta nativamente da solo. Questo metodo fornisce prestazioni più veloci di Where-Object in un comando semplice ed elegante. Come il metodo ForEach, tutti gli oggetti che vengono emessi da questo metodo vengono restituiti in una raccolta generica di type System.Raccolta.ObjectModel.Collezione1.

Esiste solo una versione di questo metodo, che può essere descritta come segue:

Where(scriptblock expression])

Come indicato dalle parentesi quadre, è richiesto il blocco script espressione e l’enumerazione modalità e l’argomento numero intero numberToReturn sono opzionali, quindi è possibile richiamare questo metodo utilizzando 1, 2 o 3 argomenti. Se si desidera utilizzare un argomento particolare, è necessario fornire tutti gli argomenti a sinistra di tale argomento (cioè se si desidera fornire un valore per numberToReturn, è necessario fornire valori anche per mode ed expression).

Where(scriptblock expression)

L’invocazione più semplice del metodo Where prende semplicemente un’espressione di blocco di script come argomento. L’espressione del blocco di script verrà valutata una volta per ogni oggetto nella raccolta che viene elaborato e, se restituisce true, l’oggetto verrà restituito dal metodo Where. Questo è l’equivalente funzionale di chiamare il cmdlet Where-Object e passargli un blocco di script. Come il cmdlet Where-Object, le variabili PS _ e PS PSItem possono essere utilizzate per fare riferimento all’elemento corrente che viene elaborato all’interno del blocco script.

Ecco un esempio molto semplice, che mostra come è possibile ottenere un elenco di servizi in esecuzione.

# Get all services$services = Get-Service# Now filter out any services that are not running$services.where{$_.Status -eq 'Running'}

Questo non offre alcuna nuova funzionalità, ma offre prestazioni molto più veloci di Where-Object e la sintassi è abbastanza facile da seguire, quindi dovresti davvero considerare questo per i tuoi script quando stai eseguendo il filtraggio lato client delle raccolte che hai memorizzato in una variabile.

Where(scriptblock expression, Whereeoperatorselectionmode mode)

Quando inizi a guardare i parametri opzionali per il metodo Where, le cose iniziano a diventare molto più interessanti. La versione 4.0 di Windows PowerShell includeva una nuova enumerazione con un nome del tipo di sistema.Gestione.Automazione.WhereOperatorSelectionMode. Nota il suffisso di quel typename: “SelectionMode”. Viene utilizzato per fornire potenti funzionalità di selezione in una sintassi Where. Ecco i valori inclusi in questa enumerazione, insieme alle loro definizioni:

Default Filtro di raccolta utilizzando l’espressione blocco di script, per un conteggio massimo se uno è stato fornito o inadempiente a tutti gli oggetti della collezione, se non il massimo conte, è stato fornito in numberToReturn.
First Restituisce i primi N oggetti che passano il filtro di blocco dello script di espressione, impostando solo 1 oggetto se non è stato richiesto un conteggio specifico in numberToReturn.
Last Restituisce gli ultimi N oggetti che passano il filtro di blocco dello script di espressione, impostando solo 1 oggetto se non è stato richiesto un conteggio specifico in numberToReturn.
SkipUntil Salta gli oggetti nella raccolta fino a quando un oggetto non passa il filtro di blocco dello script di espressione, quindi restituisce i primi N oggetti, impostando tutti gli oggetti rimanenti se non è stato fornito alcun conteggio massimo in numberToReturn.
Fino a Restituisce i primi N oggetti in una raccolta fino a quando un oggetto passa il filtro di blocco dello script di espressione, impostando tutti gli oggetti che portano al primo oggetto passato se non è stato fornito alcun conteggio massimo in numberToReturn.
Split Dividere una raccolta in due, di mettere tutti gli oggetti che passano l’espressione blocco di script filtro nella prima raccolta fino ad un massimo conte se fornito in numberToReturn, o tutti gli oggetti che passano se non conteggio massimo è stato fornito, e il posizionamento di tutti gli altri oggetti che non vengono messi nella prima raccolta in seconda collezione.

Ognuno di questi offre un valore univoco quando si elaborano raccolte di dati, quindi fornirò maggiori dettagli su ciascuna modalità di selezione di seguito.

Default

Non sorprende che il valore predefinito dell’argomento mode sia ‘Default’. La modalità di selezione predefinita offre la stessa funzionalità che si ottiene quando non si fornisce affatto una modalità di selezione. Ad esempio, avremmo potuto scrivere l’ultima riga del nostro esempio precedente in questo modo:

# Now filter out any services that are not running$services.where({$_.Status -eq 'Running'},'Default')

In questo esempio, l’argomento aggiuntivo non è necessario, perché fa esattamente la stessa cosa che farebbe se non fornissi l’argomento. È inoltre possibile fornire il numero massimo di oggetti che si desidera restituire durante l’utilizzo della modalità di selezione predefinita utilizzando l’argomento numberToReturn, in questo modo:

# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Default',10)

È importante notare che la funzionalità esatta è disponibile anche quando si utilizza la prima modalità di selezione (di cui parleremo tra un attimo), quindi non è davvero pratico utilizzare nessuno dei parametri opzionali quando si utilizza la modalità di selezione predefinita.

First

Come avrai intuito, la Prima modalità di selezione ti consente di selezionare i primi oggetti della raccolta che passano il filtro espressione blocco script. Quando si utilizza Prima senza un valore per l’argomento numberToReturn, o quando si utilizza prima con un valore di 0 per l’argomento numberToReturn, verrà restituito solo il primo oggetto che passa il filtro. È possibile specificare facoltativamente quanti oggetti restituire nell’argomento numberToReturn, nel qual caso verranno restituiti molti oggetti (supponendo che ci siano molti oggetti che passano il filtro).

Ecco alcuni esempi utilizzando la nostra collezione di servizi che mostra la prima modalità di selezione in azione:

# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First')# Get the first service in our collection that is running$services.where({$_.Status -eq 'Running'},'First',1)# Get the first 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'First',10)

Si noti che il secondo comando in questi esempi restituisce gli stessi risultati del primo comando perché passa semplicemente esplicitamente il valore predefinito dell’argomento numberToReturn quando viene utilizzata la Prima modalità di selezione.

Ultimo

L’ultima modalità di selezione funziona in modo simile alla Prima modalità di selezione, consentendo di selezionare gli ultimi oggetti della raccolta che passano il filtro espressione blocco script. Quando si utilizza Last without a value per l’argomento numberToReturn o quando si utilizza Last with a value of 0 per l’argomento numberToReturn, verrà restituito solo l’ultimo oggetto che passa il filtro. È possibile specificare facoltativamente quanti oggetti restituire nell’argomento numberToReturn, nel qual caso verranno restituiti molti oggetti (supponendo che ci siano molti oggetti che passano il filtro).

Ecco alcuni esempi utilizzando la nostra collezione di servizi che mostra l’ultima modalità di selezione in azione:

# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last')# Get the last service in our collection that is running$services.where({$_.Status -eq 'Running'},'Last',1)# Get the last 10 services in our collection that are running$services.where({$_.Status -eq 'Running'},'Last',10)

Come anche i primi esempi di modalità di selezione, il secondo comando in questi esempi restituisce gli stessi risultati del primo comando perché sta semplicemente passando esplicitamente il valore predefinito dell’argomento numberToReturn quando viene utilizzata l’ultima modalità di selezione.

SkipUntil

La modalità di selezione SkipUntil consente di saltare tutti gli oggetti in una raccolta fino a trovare uno che passa il filtro espressione blocco script. Una volta trovato un oggetto che passa il filtro, la modalità SkipUntil restituirà tutti gli oggetti rimanenti nella raccolta se non è stato fornito alcun valore o un valore pari a 0 all’argomento numberToReturn oppure restituirà i primi N oggetti rimanenti nella raccolta se è stato fornito un valore maggiore di zero all’argomento numberToReturn. In entrambi i casi, i risultati includeranno il primo oggetto che ha superato il filtro.

Ecco alcuni esempi utilizzando un sottoinsieme della nostra collezione di servizi per mostrare la modalità di selezione SkipUntil in azione:

# Get a collection of services whose name starts with "c"$services = Get-Service c*# Skip all services until we find one with a status of "Running"$services.where({$_.Status -eq 'Running'},'SkipUntil')# Skip all services until we find one with a status of "Running", then# return the first 2$services.where({$_.Status -eq 'Running'},'SkipUntil',2)

Fino a

La modalità di selezione Fino a quando fornisce la funzionalità opposta della modalità di selezione SkipUntil. Esso consente di restituire gli oggetti in una raccolta fino a trovare uno che passa il filtro di espressione del blocco di script. Una volta trovato un oggetto che passa il filtro, il metodo Where interrompe l’elaborazione degli oggetti. Se non si fornisce un valore per l’argomento numberToReturn o se si fornisce un valore pari a 0, la modalità di selezione Until restituirà tutti gli oggetti della raccolta che portano al primo che passa il filtro espressione blocco script. Se si fornisce un valore per l’argomento numberToReturn maggiore di 0, la modalità di selezione Until restituirà al massimo quel numero di oggetti, il che significa che potrebbe non trovare nemmeno un oggetto che passa il filtro di espressione del blocco di script.

Ecco alcuni esempi che utilizzano un sottoinsieme diverso della nostra collezione di servizi per mostrare la modalità di selezione Fino a quando in azione:

# Get a collection of services whose name starts with "p"$services = Get-Service p*# Return all services until we find one with a status of "Stopped"$services.where({$_.Status -eq 'Stopped'},'Until')# Return the first 2 services unless we find one with a status of# "Stopped" first$services.where({$_.Status -eq 'Stopped'},'Until',2)

Split

La modalità di selezione split è unica. Invece di restituire un sottoinsieme della raccolta con cui si inizia in una nuova raccolta, restituisce una nuova raccolta che contiene internamente due raccolte separate. Ciò che contengono quelle raccolte nidificate dipende da come si utilizza la modalità di selezione divisa. Split consente di dividere una raccolta di oggetti in due. Per impostazione predefinita, se non si fornisce un valore per l’argomento numberToReturn o se si fornisce un valore pari a 0 per l’argomento numberToReturn, Split posizionerà tutti gli oggetti che passano il filtro espressione blocco script nella prima raccolta nidificata e tutti gli altri oggetti (quelli che non passano il filtro espressione blocco script) nella seconda raccolta nidificata. Se si fornisce un valore maggiore di 0 per l’argomento numberToReturn, split limiterà la dimensione della prima raccolta a tale importo massimo e tutti gli oggetti rimanenti nella raccolta, anche quelli che corrispondono al filtro di espressione del blocco di script, verranno inseriti nella seconda raccolta.

Ecco alcuni esempi che mostrano come la modalità di selezione divisa può essere utilizzata per dividere una raccolta di oggetti in modi diversi:

# Get all services$services = Get-Service# Split the services into two groups: Running and not Running$running,$notRunning = $services.Where({$_.Status -eq 'Running'},'Split')# Show the Running services$running# Show the services that are not Running$notRunning# Split the services into the same two groups, but limit the Running group# to a maximum of 10 items$10running,$others = $services.Where({$_.Status -eq 'Running'},'Split',10)# Show the first 10 Running services$10running# Show all other services$others

Come puoi vedere da questo esempio, Split è una modalità di selezione piuttosto potente, che fornisce un mix di filtraggio, raggruppamento e selezione in una singola chiamata di comando.

Questa raccolta di modalità di selezione rende il metodo Where veloce ed elegante ma allo stesso tempo più potente di Where-Object, Group-Object e Select-Object combinati in un’unica pipeline. Cosa non c’e ‘ da amare?

Carenze nei metodi di script ForEach e Where in TypePx

Come ho accennato in precedenza in questo articolo, ho scritto estensioni di tipo per PowerShell 3.0 e versioni successive e li ha confezionati in un modulo chiamato TypePx. TypePx è un modulo di script che è stato scritto interamente in PowerShell, e funziona su PowerShell 3.0 o versioni successive. Se si utilizza PowerShell 3.0 (e solo se si utilizza PowerShell 3.0), TypePx definisce i metodi di script ForEach e Where che imitano il comportamento dei metodi ForEach e Where in PowerShell 4.0 e versioni successive. Mentre il sistema di tipo esteso in PowerShell consente di imitare questo comportamento, ci sono alcune carenze dovute all’implementazione in PowerShell che ha influito su quanto sono stato in grado di andare con queste estensioni di tipo. Questa sezione descriverà alcune delle differenze e limitazioni esistenti nei metodi di script ForEach e Where in TypePx di cui potresti voler essere a conoscenza se stai usando PowerShell 3.0.

Blocchi di script richiamati da un metodo di script eseguito in un ambito figlio

A differenza dei metodi ForEach e Where implementati come parte di PowerShell 4.0 o versioni successive che richiamano il blocco script espressione nell’ambito corrente in cui viene chiamato il metodo, i metodi ForEach e Where script implementati in PowerShell 3.0 invocano il blocco script espressione in un ambito figlio. Questa è una limitazione in PowerShell che è stata lì fin dall’inizio (una limitazione, potrei aggiungere, che penso sia di gran lunga la più grande mancanza di PowerShell come linguaggio).

A causa di questa limitazione, tutte le variabili assegnate all’interno di un blocco di script di espressione verranno modificate solo nell’ambito figlio. Ciò ha implicazioni se il blocco di script di espressione è destinato ad aggiornare una variabile nell’ambito in cui si richiama ForEach o Where. È improbabile che ciò causi un problema durante l’utilizzo di Where perché non è molto comune modificare le variabili in un blocco di script di espressione Where, ma nei blocchi di script ForEach ciò potrebbe rappresentare un problema, quindi è necessario tenerlo a mente se si utilizzano queste estensioni.

Dovrei notare che vorrei rimuovere del tutto questa limitazione, e credo che sarò in grado di farlo, tuttavia al momento in cui ho scritto questo articolo non ho ancora implementato una correzione per questo.

La maggior parte, ma non tutte le raccolte avranno metodi ForEach e Where

In PowerShell 4.0 e versioni successive, i metodi ForEach e Where sono magicamente resi disponibili per tutti i tipi che implementano IEnumerable ad eccezione di String, XmlNode e tipi che implementano IDictionary. In PowerShell, il sistema di tipi estesi non consente la creazione di estensioni per le interfacce, ma solo per i tipi. Questa è una sfida se vuoi creare un’ampia estensione come ForEach e Where. Nell’implementazione corrente di queste estensioni in TypePx, il TypePx modulo rileva tutti i tipi in tutti gli assembly caricati nell’attuale dominio dell’applicazione, e per tutti i non-tipi generici che definire IEnumerable ma non IDictionary (esclusi Stringa e XmlNode) e, inoltre, per tutti i tipi generici che definire IEnumerable ma non IDictionary per generico collezioni di PSObject, Object, String, Int32, o Int64, ForEach e Dove script metodi verrà creato.

Questo copre un gran numero di tipi che nel mio test è stato sufficiente, tuttavia è possibile imbattersi in tipi in cui si desidera utilizzare questi metodi e non sono disponibili. Se questo è il caso, fammi sapere attraverso GitHub e vedrò cosa posso fare. Questa è anche una limitazione che vorrei rimuovere, ma ho bisogno di più tempo per ricercare come implementare la funzionalità equivalente in un assembly compilato in cui potrei essere in grado di definirlo più come è definito in PowerShell 4.0 e versioni successive.

Gli script PowerShell non sono veloci come il codice compilato

Questo probabilmente va da sé, ma quando scrivi qualcosa in PowerShell, che è un linguaggio interpretato, non verrà eseguito così rapidamente come se scrivessi la logica equivalente in un linguaggio come c#. Questo è assolutamente il caso dei metodi di script ForEach e Where, che utilizzano internamente ForEach-Object, Where-Object e pipelining per imitare il comportamento dei metodi nativi ForEach e Where. In questo caso, il vantaggio di avere questi comandi deriva dall’elegante sintassi e funzionalità che forniscono, oltre a poterli utilizzare negli script per PowerShell 3.0 e 4.0. I vantaggi delle prestazioni in ForEach e Where sono solo nell’implementazione nativa di PowerShell 4.0.

PowerShell 3.0 richiede parentesi attorno a tutti i parametri del metodo

Ho menzionato negli esempi sopra che sono stato in grado di invocare un metodo con un singolo parametro di blocco di script letterale senza avvolgere quel blocco di script letterale in parentesi aggiuntive. Tale capacità esiste solo in PowerShell 4.0 o successivo a causa di miglioramenti apportati al parser in quella versione. In PowerShell 3.0, il parser non supporta questo, quindi le parentesi sono sempre necessarie affinché i metodi di script ForEach e Where funzionino con un singolo parametro di blocco di script letterale in quella versione.

Conclusione

Quando ho iniziato a provare a imitare il comportamento di ForEach e Where magic methods in PowerShell 3.0, non mi sono reso conto di quanta funzionalità avessero fornito. Scavare nei dettagli tecnici dietro questi metodi in modo da poter creare le estensioni che volevo in TypePx ha aiutato a scoprire tutte le funzionalità nascoste in questi metodi molto potenti, e sono molto felice di condividere quelli con voi in questo articolo. Spero che queste informazioni ti aiutino a sfruttare questo meraviglioso nuovo set di funzionalità nel tuo lavoro PowerShell, anche se stai ancora utilizzando PowerShell 3.0. Scripting felice!