Articles

ForEach și în cazul în care metodele magice

ForEach și în cazul în care sunt două concepte utilizate frecvent, care au fost disponibile în PowerShell, deoarece versiunea 1 a ieșit în 2006. ForEach a fost disponibil atât ca o declarație și un cmdlet (ForEach-obiect), permițându-vă să itera printr-o colecție de obiecte și să ia unele măsuri o dată pentru fiecare obiect din acea colecție. Unde a fost disponibil ca cmdlet (Where-Object), permițându-vă să filtrați elementele dintr-o colecție care nu trec de o anumită condiție, o condiție care poate fi evaluată folosind fie proprietățile obiectelor din colecție, fie obiectele în sine care aparțin colecției. Capacitatea ForEach de a acționa asupra articolelor dintr-o colecție și capacitatea Where de a filtra o colecție sunt caracteristici care sunt extrem de utile și sunt incluse într-o formă sau alta în logica din spatele multor scripturi, comenzi și module PowerShell, indiferent de ce versiune de PowerShell este utilizată. De fapt, sunt atât de utilizate încât au fost o zonă de interes pentru îmbunătățirea performanței, funcționalității și sintaxei în versiunile PowerShell 3.0 și 4.0.

odată cu lansarea Windows PowerShell 4.0, au fost introduse două noi metode „magice” pentru tipurile de colecții care oferă o nouă sintaxă pentru accesarea capacităților ForEach și Where în Windows PowerShell. Aceste metode sunt numite în mod corespunzătorfiecare și unde. Eu numesc aceste metode „magice” pentru că sunt destul de magice în modul în care funcționează în PowerShell. Acestea nu apar în Get-Member output, chiar dacă aplicați-Force și request-MemberType All. Dacă vă suflecați mânecile și săpați cu reflecție, le puteți găsi; cu toate acestea, este nevoie de o căutare largă, deoarece acestea sunt metode de extensie private implementate pe o clasă privată. Cu toate acestea, chiar dacă nu pot fi descoperite fără a trage cu ochiul sub copertine, ele sunt acolo atunci când aveți nevoie de ele, sunt mai rapide decât omologii lor mai în vârstă și includ funcționalități care nu erau disponibile la omologii lor mai în vârstă, de unde și sentimentul „magic” cu care te lasă atunci când le folosești în PowerShell. Din păcate, aceste metode rămân nedocumentate chiar și astăzi, la aproape un an de când au fost lansate public, atât de mulți oameni nu își dau seama de puterea disponibilă în aceste metode. Acest articol va încerca să corecteze acest lucru explicând unde pot fi utilizate și cum funcționează, astfel încât să puteți utiliza această magie atunci când utilizați PowerShell.

o notă despre PowerShell 3.0

înainte de a intra în a explica modul în care ForEach și în cazul în care metodele de lucru, trebuie să menționez ceva cu privire la aceste două metode și PowerShell 3.0. Deși este adevărat că ForEach și în cazul în care metodele au fost puse la dispoziție numai în PowerShell 4.0 și versiunile ulterioare, PowerShell 3.0 este încă foarte utilizat pe scară largă în multe medii și, cu excepția cazului în care utilizați PowerShell într-un mediu standardizat pe PowerShell 4.0 și versiuni ulterioare, s-ar putea să vă doriți să puteți profita de sintaxa oferită de noile metode atunci când utilizați PowerShell 3.0. Am simțit că aceasta este o limitare care merită abordată, așa că, ca parte a modulului TypePx pe care l-am publicat recent pe Github și în Galeria de resurse PowerShell (aka depozitul public PowerShellGet, în prezent în Previzualizare limitată), am inclus ForEach și unde metode de script care sunt echivalente funcțional cu metodele introduse în PowerShell 4.0, astfel încât să puteți utiliza noua sintaxă și funcționalitate chiar dacă utilizați PowerShell 3.0. Există câteva deficiențe în această implementare, pe care le voi evidenția mai târziu în acest articol.

metoda ForEach

ForEach este o metodă care vă permite să iterați rapid printr-o colecție de obiecte și să acționați asupra fiecărui obiect din acea colecție. Această metodă oferă performanțe mai rapide decât omologii săi mai vechi (instrucțiunea foreach și cmdletul ForEach-Object) și simplifică, de asemenea, unele dintre cele mai frecvente acțiuni pe care poate doriți să le întreprindeți asupra obiectelor din colecție. Orice obiecte care sunt emise prin această metodă sunt returnate într-o colecție generică de sistem de tip.Colecții.ObjectModel.Colecția 1.

există șase modalități acceptate prin care puteți invoca această metodă și fiecare dintre acestea va fi explicată mai detaliat mai jos. Argumentele acceptate care pot fi utilizate la invocarea metodei ForEach sunt următoarele:

  • ForEach(Expresie scriptblock)
  • ForEach(tip convertToType)
  • ForEach(string propertyName)
  • ForEach(string propertyName, obiect newValue)
  • ForEach(string methodName)
  • ForEach(string methodName, argumente obiect)
  • ForEach(Expresie scriptblock, argumente obiect)
  • ForEach (Expresie scriptblock, argumente obiect)

rețineți că acestea sunt perechi de argumente acceptate, nu supraîncărcări diferite disponibile pentru metoda foreach. Utilizarea oricăror perechi de argumente, altele decât acestea, poate duce la erori care nu identifică în mod clar care este problema reală.

ForEach(scriptblock expression) și ForEach(scriptblock expression, object arguments)

dacă treceți o expresie de bloc de script în metoda ForEach, puteți efectua același tip de sarcini pe care le-ați face într-un bloc de script pe care l-ați folosi cu instrucțiunea foreach sau cmdletul ForEach-Object. De asemenea, la fel ca cmdletul ForEach-Object, variabilele $_ și $PSItem fac referire la elementul curent care este procesat. Orice argumente pe care le furnizați dincolo de argumentul inițial al blocului de script vor fi utilizate ca argumente pentru blocul de script. Acest lucru este la fel ca modul în care parametrul-ArgumentList funcționează pe cmdletul ForEach-Object. Aici este un exemplu care demonstrează modul în care s-ar putea folosi acest lucru pentru a executa un bloc script pe fiecare element dintr-o colecție:

# 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')

este posibil să fi observat ceva ciudat despre această sintaxă, pentru că nu am wrap blocul script în sine în paranteze. Puteți să-l înfășurați în paranteze rotunde, însă acest lucru este opțional în PowerShell 4.0 sau o versiune ulterioară, deoarece parserul PowerShell a fost îmbunătățit pentru a permite omiterea parantezelor ori de câte ori invocați o metodă care acceptă un singur argument de blocare a scriptului. De asemenea, la fel ca instrucțiunea foreach și cmdletul ForEach-Object, blocul de script pe care îl furnizați este invocat în domeniul de aplicare curent. Asta înseamnă că orice atribuiri variabile pe care le faceți în interiorul acelui bloc de script vor persista după ce metoda ForEach a terminat executarea.

ForEach(tip convertToType)

unic pentru metoda ForEach, puteți trece un tip în metoda ForEach dacă doriți să convertiți fiecare element dintr-o colecție într-un alt tip. De exemplu, imaginați-vă că aveți o colecție de obiecte și doriți să convertiți acele obiecte în echivalentul lor de șir. Iată cum ar arăta asta cu metoda ForEach:

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

ați fi putut efectua aceeași sarcină tastând colecția într-o serie de șiruri de tip (de exemplu,] $procese), iar tipărirea matricei este de fapt semnificativ mai rapidă, cu toate acestea există șanse foarte mari să nu observați nici măcar diferența de timp de execuție dacă nu lucrați cu o colecție foarte, foarte mare. În ciuda diferenței de timp, voi tinde să prefer sintaxa metodei ForEach în anumite situații dacă îmi permite să mențin eleganța în implementare, evitând paranteze rotunde suplimentare în scripturile pe care le scriu.

ForEach(string propertyName)

în PowerShell 3.0 și mai târziu, un al doilea set de parametri a fost adăugat la ForEach-Object pentru a vă permite să preluați mai ușor valoarea unei proprietăți specifice prin simpla trecere într-un nume de proprietate ca singura valoare parametru pentru ForEach-Object. Această comoditate a fost oferită și în metoda ForEach. Iată un exemplu care demonstrează modul în care puteți itera printr-o colecție și returnați o proprietate a acelei colecții:

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

desigur, de la versiunea 3.0 a PowerShell, puteți invoca pur și simplu $services.Name pentru a obține numele tuturor serviciilor și care se vor finaliza mai repede decât alternativa metodei ForEach (deși veți observa doar diferența de performanță în colecții foarte mari, de ordinul a sute de mii de obiecte); cu toate acestea, acest lucru funcționează numai pentru proprietăți care nu se află în colecția în sine și este o sintaxă cu care unii scriptori nu sunt confortabili datorită naturii implicite a ceea ce face comanda. Noua sintaxă a metodei ForEach vă oferă o alternativă mai explicită, care are avantajul suplimentar de a fi și mai puțin auto-documentare.

ForEach(string propertyName, obiect newValue)

nu numai că puteți prelua o proprietate pe o colecție de obiecte, puteți seta o proprietate pe o colecție de obiecte, de asemenea. Aceasta este o funcționalitate care nu este disponibilă în celelalte foreach, cu excepția cazului în care creați în mod explicit blocul de script pentru a face acest lucru. Pentru a seta proprietatea, pur și simplu furnizați numele proprietății și valoarea pe care doriți să o utilizați atunci când setați acea proprietate, astfel:

# 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')

la fel ca atribuirile pe care le-ați face folosind operatorul egal, PowerShell va încerca să convertească orice furnizați ca valoare nouă în tipul adecvat pentru proprietatea atribuită.

ForEach(string methodName) și ForEach(string methodName, object arguments)

pentru a invoca o metodă, pur și simplu furnizați numele metodei ca primul argument și apoi argumentele pentru acea metodă ca al doilea, al treilea, al patrulea etc. argumente. În cazul în care metoda nu ia nici un argument, puteți trece pur și simplu în numele metodei și va fi invocată fără argumente. Iată un exemplu care arată cum ai putea ucide o grămadă de procese care rulează un anumit program:

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

Iată un alt exemplu, de data aceasta folosind o metodă cu argumente în timp ce arătați cum puteți verifica dacă comenzile respectă cele mai bune practici folosind nume și pseudonime adecvate pentru parametrii utilizați în mod obișnuit:

# 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

după cum puteți vedea din aceste rezultate, există cu siguranță unele inconsecvențe în implementarea parametrilor ComputerName care ar trebui corectați.

care acoperă toate funcționalitățile care sunt disponibile în prezent în metoda ForEach. După cum puteți vedea, nu există o mulțime de funcționalități noi oferite în această metodă, dar îmbunătățirile sintaxei atunci când efectuați o sarcină simplă pe o colecție de obiecte sunt frumoase, iar îmbunătățirile de performanță ale metodei ForEach în comparație cu declarația echivalentă foreach pentru ForEach-Object pipeline sunt cu siguranță o îmbunătățire binevenită. Cu această explicație din drum, să trecem la metoda Where.

metoda Where

Where este o metodă care vă permite să filtrați o colecție de obiecte. Acest lucru seamănă foarte mult cu cmdletul Where-Object, dar metoda Where este, de asemenea, ca Select-Object și Group-Object, include mai multe caracteristici suplimentare pe care cmdletul Where-Object nu le acceptă nativ de la sine. Această metodă oferă performanțe mai rapide decât Where-Object într-o comandă simplă și elegantă. La fel ca metoda ForEach, orice obiecte care sunt afișate prin această metodă sunt returnate într-o colecție generică de sistem de tip.Colecții.ObjectModel.Colecție1.

există o singură versiune a acestei metode, care poate fi descrisă după cum urmează:

Where(scriptblock expression])

după cum indică parantezele pătrate, este necesar blocul de scripturi de Expresie, iar enumerarea modului și argumentul întreg numberToReturn sunt opționale, astfel încât să puteți invoca această metodă folosind 1, 2 sau 3 argumente. Dacă doriți să utilizați un anumit argument, trebuie să furnizați toate argumentele din stânga acelui argument (adică dacă doriți să furnizați o valoare pentru numberToReturn, trebuie să furnizați și valori pentru MOD și expresie).

unde(scriptblock expression)

invocarea cea mai de bază a metodei Where ia pur și simplu o expresie de bloc de script ca argument. Expresia blocului de script va fi evaluată o dată pentru fiecare obiect din colecția care este procesată și, dacă returnează true, obiectul va fi returnat prin metoda Where. Acesta este echivalentul funcțional al apelării cmdlet-ului Where-Object și al transmiterii unui bloc de script. La fel ca cmdletul Where-Object, variabilele $_ și $PSItem pot fi utilizate pentru a se referi la elementul curent care este procesat în interiorul blocului script.

Iată un exemplu foarte simplu, care arată cum puteți obține o listă de servicii care rulează.

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

aceasta nu oferă nicio funcționalitate nouă, dar oferă performanțe mult mai rapide decât Where-Object și sintaxa este destul de ușor de urmărit, așa că ar trebui să luați în considerare acest lucru pentru scripturile dvs. atunci când efectuați filtrarea pe partea clientului a colecțiilor pe care le-ați stocat într-o variabilă.

unde(scriptblock expression, whereoperatorselectionmode mode)

când începeți să vă uitați la parametrii opționali pentru metoda Where, lucrurile încep să devină mult mai interesante. Windows PowerShell versiunea 4.0 a inclus o nouă enumerare cu un nume de tip al sistemului.Management.Automatizare.În cazul în care operatorselectionmode. Rețineți sufixul acelui tipnume:”SelectionMode”. Este folosit pentru a oferi capabilități de selecție puternice într-o sintaxă Where. Iată valorile incluse în această enumerare, împreună cu definițiile acestora:

Default filtrați colecția folosind blocul de scripturi de Expresie, la un număr maxim dacă unul a fost furnizat sau implicit pentru toate obiectele din colectarea în cazul în care nu a fost furnizat număr maxim în numbertoreturn.
mai întâi returnați primele n obiecte care trec filtrul blocului de scripturi de Expresie, implicit la numai 1 Obiect dacă nu a fost solicitat un număr specific în numberToReturn.
Last returnați Ultimele n obiecte care trec filtrul blocului de scripturi de Expresie, implicit la numai 1 Obiect dacă nu a fost solicitat un număr specific în numberToReturn.
SkipUntil săriți obiectele din colecție până când un obiect trece filtrul blocului de scripturi de expresie și apoi returnați primele n obiecte, implicit la toate obiectele rămase dacă nu a fost furnizat un număr maxim în numberToReturn.
până la returnați primele n obiecte dintr-o colecție până când un obiect trece filtrul blocului de scripturi de Expresie, implicit la toate obiectele care au dus la primul obiect care a trecut dacă nu a fost furnizat un număr maxim în numberToReturn.
împărțiți împărțiți o colecție în două, plasând toate obiectele care trec filtrul blocului de scripturi de Expresie în prima colecție până la un număr maxim dacă unul a fost furnizat în numberToReturn sau toate obiectele care trec dacă nu a fost furnizat un număr maxim și plasând toate celelalte obiecte care nu sunt puse în prima colecție în a doua colecție colecție.

fiecare dintre acestea oferă o valoare unică atunci când procesați colecții de date, așa că vă voi oferi mai multe detalii despre fiecare mod de selecție de mai jos.

implicit

nu este surprinzător că valoarea implicită a argumentului modului este ‘implicit’. Modul de selecție implicit oferă aceeași funcționalitate pe care o obțineți atunci când nu furnizați deloc un mod de selecție. De exemplu, am fi putut scrie ultima linie a exemplului nostru anterior astfel:

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

în acest exemplu, argumentul suplimentar nu este totuși necesar, deoarece face exact același lucru pe care l-ar face dacă nu ați furniza argumentul. De asemenea, puteți furniza numărul maxim de obiecte pe care doriți să le returnați în timp ce utilizați modul de selecție implicit utilizând argumentul numberToReturn, astfel:

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

este important să rețineți că funcționalitatea exactă este disponibilă și atunci când utilizați primul mod de selecție (despre care vom vorbi într-o clipă), deci nu este practic să utilizați deloc niciunul dintre parametrii opționali atunci când utilizați modul de selecție implicit.

mai întâi

după cum probabil ați ghicit, primul mod de selecție vă permite să selectați primele obiecte din colecție care trec filtrul de expresie a blocului de script. Când utilizați mai întâi fără o valoare pentru argumentul numberToReturn sau când utilizați mai întâi cu o valoare de 0 pentru argumentul numberToReturn, numai primul obiect care trece filtrul va fi returnat. Puteți specifica opțional câte obiecte să se întoarcă în argumentul numberToReturn, caz în care multe obiecte vor fi returnate (presupunând că există multe obiecte care trec filtrul).

iată câteva exemple folosind colecția noastră de servicii care arată primul mod de selecție în acțiune:

# 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)

rețineți că a doua comandă din aceste exemple returnează aceleași rezultate ca prima comandă, deoarece pur și simplu trece în mod explicit valoarea implicită a argumentului numberToReturn atunci când este utilizat primul mod de selecție.

Last

ultimul mod de selecție funcționează la fel ca primul mod de selecție, permițându-vă să selectați ultimul obiect(e) din colecția care trece filtrul de Expresie bloc script. Când utilizați Last fără o valoare pentru argumentul numberToReturn sau când utilizați Last cu o valoare de 0 pentru argumentul numberToReturn, numai ultimul obiect care trece filtrul va fi returnat. Puteți specifica opțional câte obiecte să se întoarcă în argumentul numberToReturn, caz în care multe obiecte vor fi returnate (presupunând că există multe obiecte care trec filtrul).

iată câteva exemple folosind colecția noastră de servicii care arată ultimul mod de selecție în acțiune:

# 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)

de asemenea, la fel ca primele exemple de mod de selecție, a doua comandă din aceste exemple returnează aceleași rezultate ca prima comandă, deoarece pur și simplu trece în mod explicit valoarea implicită a argumentului numberToReturn când se utilizează ultimul mod de selecție.

SkipUntil

modul de selecție SkipUntil vă permite să săriți toate obiectele dintr-o colecție până când găsiți unul care trece filtrul de Expresie bloc script. Odată ce ați găsit un obiect care trece filtrul, modul SkipUntil va returna toate obiectele rămase în colecție dacă nu a fost furnizată nicio valoare sau o valoare de 0 argumentului numberToReturn sau va returna primele n obiecte rămase în colecție dacă o valoare mai mare decât zero a fost furnizată argumentului numberToReturn. În ambele cazuri, rezultatele vor include primul obiect care a trecut filtrul.

iată câteva exemple folosind un subset al colecției noastre de servicii pentru a afișa modul de selecție SkipUntil în acțiune:

# 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)

până la

modul de selecție până oferă funcționalitatea opusă modului de selecție SkipUntil. Acesta vă permite să se întoarcă obiecte într-o colecție până când găsiți unul care trece filtrul de Expresie bloc script. Odată ce găsiți un obiect care trece filtrul, metoda Where oprește procesarea obiectelor. Dacă nu furnizați o valoare pentru argumentul numberToReturn sau dacă furnizați o valoare de 0, până la modul de selecție va returna toate obiectele din colecția care duce la primul care trece filtrul de Expresie bloc script. Dacă furnizați o valoare pentru argumentul numberToReturn care este mai mare decât 0, până la modul de selecție va reveni cel mult acel număr de obiecte, ceea ce înseamnă că nu poate găsi chiar și un obiect care trece filtrul de Expresie bloc script.

iată câteva exemple folosind un subset diferit al colecției noastre de servicii pentru a afișa modul de selecție până în acțiune:

# 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

modul de selecție Split este unic. În loc să returneze un subset al colecției cu care începeți într-o colecție nouă, returnează o colecție nouă care conține intern două colecții separate. Ce conțin aceste colecții imbricate depinde de modul în care utilizați modul de selecție divizat. Split vă permite să împărțiți o colecție de obiecte în două. În mod implicit, dacă nu furnizați o valoare pentru argumentul numberToReturn sau dacă furnizați o valoare de 0 pentru argumentul numberToReturn, Split va plasa toate obiectele care trec filtrul de Expresie bloc de script în prima colecție imbricată și toate celelalte obiecte (cele care nu trec filtrul de Expresie bloc de script) în a doua colecție imbricată. Dacă furnizați o valoare mai mare de 0 pentru argumentul numberToReturn, split va limita dimensiunea primei colecții la acea sumă maximă și toate obiectele rămase din colecție, chiar și cele care se potrivesc cu filtrul de Expresie bloc script, vor fi plasate în a doua colecție.

iată câteva exemple care arată modul în care modul de selecție divizat poate fi utilizat pentru a împărți o colecție de obiecte moduri diferite:

# 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

după cum puteți vedea din acest exemplu, Split este un mod de selecție destul de puternic, oferind un amestec de filtrare, grupare și selecție într-un singur apel de comandă.

această colecție de moduri de selecție face metoda Where rapidă și elegantă, dar în același timp mai puternică decât Where-Object, Group-Object și Select-Object combinate într-o singură conductă. Ce nu e de iubit la asta?

deficiențe în ForEach și în cazul în care metodele de script în TypePx

așa cum am menționat mai devreme în acest articol, am scris extensii de tip pentru PowerShell 3.0 și mai târziu și ambalate-le într-un modul numit TypePx. TypePx este un modul de script care a fost scris în întregime în PowerShell și rulează pe PowerShell 3.0 sau o versiune ulterioară. Dacă utilizați PowerShell 3.0 (și numai dacă utilizați PowerShell 3.0), TypePx definește ForEach și unde metode de script care imită comportamentul ForEach și unde metode în PowerShell 4.0 și mai târziu. În timp ce sistemul de tip extins din PowerShell face posibilă imitarea acestui comportament, există câteva deficiențe datorate implementării în PowerShell care au afectat cât de departe am putut merge cu aceste extensii de tip. Această secțiune va descrie unele dintre diferențele și limitările care există în ForEach și unde metodele de script în TypePx pe care poate doriți să le cunoașteți dacă utilizați PowerShell 3.0.

blocuri de Script invocate dintr-o metodă de script rula într-un domeniu copil

spre deosebire de ForEach și în cazul în care metodele implementate ca parte a PowerShell 4.0 sau mai târziu, care invocă blocul script expresie în domeniul de aplicare curent în cazul în care se numește metoda, ForEach și în cazul în care metodele script implementate în PowerShell 3.0 invoca blocul script expresie într-un domeniu copil. Aceasta este o limitare în PowerShell care a fost acolo încă de la început (o limitare, aș putea adăuga, care cred că este de departe cel mai mare neajuns al PowerShell ca limbă).

Din cauza acestei limitări, orice variabile pe care le atribuiți în interiorul unui bloc de script Expresie vor fi modificate numai în domeniul de aplicare copil. Acest lucru are implicații în cazul în care blocul script expresie este destinat să actualizeze o variabilă în domeniul de aplicare în care invoca ForEach sau în cazul în care. Este puțin probabil ca acest lucru să cauzeze o problemă în timp ce utilizați Where, deoarece nu este foarte obișnuit să modificați variabilele într-un bloc de script de Expresie Where, dar în blocurile de script ForEach acest lucru poate reprezenta o problemă, deci trebuie să țineți cont de acest lucru dacă utilizați aceste extensii.

ar trebui să rețineți că aș dori să elimine această limitare cu totul, și cred că voi fi capabil să facă acest lucru, cu toate acestea, în momentul în care am scris acest articol nu am implementat încă o soluție pentru acest lucru.

cele mai multe, dar nu toate colecțiile vor avea ForEach și unde metodele

în PowerShell 4.0 și mai târziu, ForEach și unde metodele sunt puse la dispoziție în mod magic pentru toate tipurile care implementează IEnumerable, cu excepția String, XmlNode și tipurile care implementează IDictionary. În PowerShell, sistemul de tip extins nu permite crearea de extensii pentru interfețe, doar pentru tipuri. Aceasta este o provocare dacă doriți să creați o extensie largă, cum ar fi ForEach și Where. În implementarea curentă a acestor extensii în TypePx, modulul typepx găsește toate tipurile în toate ansamblurile încărcate în domeniul aplicației curente și pentru toate tipurile non-generice care definesc IEnumerable dar nu IDictionary (cu excepția String și XmlNode), plus pentru toate tipurile generice care definesc IEnumerable dar nu IDictionary pentru colecțiile generice de PSObject, Object, String, Int32 sau Int64, ForEach și Where script methods will be create.

aceasta acoperă un număr mare de tipuri care, în propria mea testare a fost suficient, cu toate acestea s-ar putea rula în tipuri în cazul în care doriți să utilizați aceste metode și nu sunt disponibile. Dacă acesta este cazul, anunțați-mă prin GitHub și voi vedea ce pot face. Aceasta este, de asemenea, o limitare pe care aș dori să o elimin, dar am nevoie de mai mult timp pentru a cerceta cum să implementez funcționalitatea echivalentă într-un ansamblu compilat, unde aș putea să o definesc mai mult ca și cum ar fi definită în PowerShell 4.0 și mai târziu.

scripturile PowerShell nu sunt la fel de rapide ca codul compilat

probabil că acest lucru este de la sine înțeles, dar atunci când scrieți ceva în PowerShell, care este un limbaj interpretat, acesta nu va rula la fel de repede cum ar fi dacă ați scrie logica echivalentă într-o limbă precum C#. Acest lucru este absolut cazul pentru ForEach și where script metode, care utilizează intern ForEach-Object, Where-Object și pipelining pentru a imita comportamentul ForEach nativ și Where metode. În acest caz, avantajul de a avea aceste comenzi provine din sintaxa elegantă și funcționalitatea pe care o oferă, plus posibilitatea de a le utiliza în scripturi pentru PowerShell 3.0 și 4.0. Beneficiile de performanță în ForEach și unde sunt doar în implementarea nativă PowerShell 4.0.

PowerShell 3.0 necesită paranteze în jurul tuturor parametrilor metodei

am menționat în exemplele de mai sus că am putut invoca o metodă cu un singur parametru de bloc de script literal fără a înfășura acel bloc de script literal în paranteze suplimentare. Această capacitate există doar în PowerShell 4.0 sau o versiune ulterioară datorită îmbunătățirilor aduse parserului în acea versiune. În PowerShell 3.0, parserul nu acceptă acest lucru, deci parantezele sunt întotdeauna necesare pentru ca metodele ForEach și where script să funcționeze cu un singur parametru literal script block în acea versiune.

concluzie

când am început să încerc să imit comportamentul ForEach și unde metode magice în PowerShell 3.0, nu mi-am dat seama cât de multă funcționalitate au furnizat. Săpat în detaliile tehnice din spatele acestor metode, astfel încât am putut crea extensiile am vrut în TypePx ajutat descoperi toate caracteristicile ascunse în aceste metode foarte puternice, și eu sunt foarte fericit să împărtășesc cele cu tine în acest articol. Sper că aceste informații vă ajută să utilizați acest nou set minunat de caracteristici în munca dvs. PowerShell, chiar dacă utilizați în continuare PowerShell 3.0. Scripting fericit!