Articles

ForEach und Where magic methods

ForEach und Where sind zwei häufig verwendete Konzepte, die in PowerShell seit Version 1 im Jahr 2006 verfügbar sind. ForEach war sowohl als Anweisung als auch als Cmdlet (ForEach-Object) verfügbar, sodass Sie eine Sammlung von Objekten durchlaufen und für jedes Objekt in dieser Sammlung einmal eine Aktion ausführen können. Where war als Cmdlet (Where-Object) verfügbar, mit dem Sie Elemente in einer Sammlung herausfiltern können, die eine Bedingung nicht bestehen, eine Bedingung, die entweder mithilfe der Eigenschaften von Objekten in der Sammlung oder der Objekte selbst ausgewertet werden kann, die zur Sammlung gehören. Die ForEach-Funktion zum Ausführen von Aktionen für Elemente in einer Sammlung und die Where-Funktion zum Filtern einer Sammlung sind äußerst nützliche Funktionen, die in der einen oder anderen Form in der Logik hinter vielen PowerShell-Skripten, -Befehlen und -modulen enthalten sind, unabhängig davon, welche Version von PowerShell verwendet wird. Tatsächlich werden sie so häufig verwendet, dass sie in den PowerShell-Versionen 3.0 und 4.0 ein Schwerpunkt für die Verbesserung der Leistung, Funktionalität und Syntax waren.

Mit der Veröffentlichung von Windows PowerShell 4.0 wurden zwei neue „magische“ Methoden für Auflistungstypen eingeführt, die eine neue Syntax für den Zugriff auf ForEach- und Where-Funktionen in Windows PowerShell bereitstellen. Diese Methoden werden treffend ForEach und Where genannt. Ich nenne diese Methoden „Magie“, weil sie in PowerShell ziemlich magisch funktionieren. Sie werden nicht in der Get-Member-Ausgabe angezeigt, selbst wenn Sie apply -Force und request -MemberType All . Es erfordert jedoch eine umfassende Suche, da es sich um private Erweiterungsmethoden handelt, die in einer privaten Klasse implementiert sind. Obwohl sie nicht auffindbar sind, ohne unter die Decke zu schauen, sind sie da, wenn Sie sie brauchen, sie sind schneller als ihre älteren Kollegen und sie enthalten Funktionen, die in ihren älteren Kollegen nicht verfügbar waren, daher das „magische“ Gefühl, das sie hinterlassen Sie mit, wenn Sie sie in PowerShell verwenden. Leider bleiben diese Methoden auch heute noch undokumentiert, fast ein Jahr seit ihrer Veröffentlichung, so dass viele Menschen die Macht, die in diesen Methoden verfügbar ist, nicht erkennen. In diesem Artikel wird versucht, dies zu korrigieren, indem erläutert wird, wo sie verwendet werden können und wie sie funktionieren, damit Sie diese Magie nutzen können, wenn Sie PowerShell verwenden.

Ein Hinweis zu PowerShell 3.0

Bevor ich erkläre, wie ForEach und wo Methoden funktionieren, muss ich etwas in Bezug auf diese beiden Methoden und PowerShell 3.0 erwähnen. Es stimmt zwar, dass die Methoden ForEach und Where nur in PowerShell 4.0 und späteren Versionen, PowerShell 3, verfügbar gemacht wurden.0 wird in vielen Umgebungen immer noch sehr häufig verwendet, und wenn Sie PowerShell nicht in einer Umgebung verwenden, die auf PowerShell 4.0 und höher standardisiert ist, möchten Sie möglicherweise die Syntax der neuen Methoden nutzen, wenn Sie PowerShell 3.0 verwenden. Als Teil des TypePx-Moduls, das ich kürzlich auf GitHub und in der PowerShell-Ressourcengalerie (auch bekannt als das öffentliche PowerShellGet-Repository, das sich derzeit in einer begrenzten Vorschau befindet) veröffentlicht habe, habe ich ForEach- und Where-Skriptmethoden aufgenommen, die funktional den in PowerShell 4.0 eingeführten Methoden entsprechen, sodass Sie die neue Syntax und Funktionalität auch dann nutzen können, wenn Sie PowerShell 3.0 verwenden. Es gibt einige Mängel in dieser Implementierung, die ich später in diesem Artikel hervorheben werde.

Die ForEach-Methode

ForEach ist eine Methode, mit der Sie schnell durch eine Sammlung von Objekten iterieren und einige Aktionen für jedes Objekt in dieser Sammlung ausführen können. Diese Methode bietet eine schnellere Leistung als ihre älteren Gegenstücke (die foreach-Anweisung und das ForEach-Object-Cmdlet) und vereinfacht auch einige der häufigsten Aktionen, die Sie möglicherweise für die Objekte in der Auflistung ausführen möchten. Alle Objekte, die von dieser Methode ausgegeben werden, werden in einer generischen Sammlung vom Typ System zurückgegeben.Sammlung.ObjectModel.Sammlung1.

Es gibt sechs unterstützte Methoden, mit denen Sie diese Methode aufrufen können. Die unterstützten Argumente, die beim Aufrufen der ForEach-Methode verwendet werden können, lauten wie folgt:

  • ForEach(Skriptblock-Ausdruck)
  • ForEach(Typ convertToType)
  • ForEach(String-Eigenschaftsname)
  • ForEach(String-Eigenschaftsname, Objekt newValue)
  • ForEach(String-Methodenname)
  • ForEach(String-Methodenname, Objektargumente)
  • ForEach(Skriptblock-Ausdruck, Objektargumente)

Beachten Sie, dass dies unterstützte Argumentpaarungen sind, keine unterschiedlichen Überladungen, die für die ForEach-Methode verfügbar sind. Die Verwendung anderer Argumentpaarungen kann zu Fehlern führen, die das eigentliche Problem nicht eindeutig identifizieren.

ForEach(scriptblock-Ausdruck) und ForEach(scriptblock-Ausdruck, Objektargumente)

Wenn Sie einen Skriptblockausdruck an die ForEach-Methode übergeben, können Sie dieselben Aufgaben ausführen, die Sie in einem Skriptblock ausführen würden, den Sie mit der foreach-Anweisung oder dem ForEach-Object-Cmdlet verwenden würden. Wie das Cmdlet ForEach-Object verweisen auch die Variablen $_ und $PSItem auf das aktuelle Element, das verarbeitet wird. Alle Argumente, die Sie über das anfängliche Skriptblockargument hinaus angeben, werden als Argumente für den Skriptblock verwendet. Dies entspricht genau der Funktionsweise des Parameters -ArgumentList für das Cmdlet ForEach-Object. Hier ist ein Beispiel, das zeigt, wie Sie damit einen Skriptblock für jedes Element in einer Sammlung ausführen können:

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

Möglicherweise haben Sie etwas Seltsames an dieser Syntax bemerkt, da ich den Skriptblock selbst nicht in Klammern gesetzt habe. Dies ist jedoch in PowerShell 4.0 oder höher optional, da der PowerShell-Parser erweitert wurde, damit die Klammern weggelassen werden können, wenn Sie eine Methode aufrufen, die ein einzelnes Skriptblockargument akzeptiert. Wie die foreach-Anweisung und das Cmdlet ForEach-Object wird auch der von Ihnen bereitgestellte Skriptblock im aktuellen Bereich aufgerufen. Das bedeutet, dass alle Variablenzuweisungen, die Sie innerhalb dieses Skriptblocks vornehmen, bestehen bleiben, nachdem die Ausführung der ForEach-Methode abgeschlossen ist.

ForEach(type convertToType)

Einzigartig für die ForEach-Methode können Sie einen Typ an die ForEach-Methode übergeben, wenn Sie jedes Element in einer Sammlung in einen anderen Typ konvertieren möchten. Stellen Sie sich beispielsweise vor, Sie haben eine Sammlung von Objekten und möchten diese Objekte in ihr String-Äquivalent konvertieren. Hier ist, wie das mit der ForEach-Methode aussehen würde:

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

Sie hätten die gleiche Aufgabe ausführen können, indem Sie die Sammlung in ein Array vom Typ string (zB ]$processes ) , und das Array typisieren ist in der Tat deutlich schneller, aber es gibt eine sehr gute Chance, dass Sie den Unterschied in der Ausführungszeit nicht einmal bemerken würden, es sei denn, Sie arbeiteten mit einer sehr, sehr großen Sammlung. Trotz des Zeitunterschieds bevorzuge ich in bestimmten Situationen die Syntax der ForEach-Methode, wenn ich die Eleganz der Implementierung beibehalten kann, indem ich zusätzliche runde Klammern in den von mir geschriebenen Skripten vermeide.

ForEach(string PropertyName)

In PowerShell 3.0 und höher wurde ForEach-Object ein zweiter Parametersatz hinzugefügt, mit dem Sie den Wert einer bestimmten Eigenschaft einfacher abrufen können, indem Sie einfach einen Eigenschaftsnamen als einzigen Parameterwert für ForEach-Object übergeben. Diese Bequemlichkeit wurde auch in der ForEach-Methode angeboten. Hier ist ein Beispiel, das zeigt, wie Sie eine Sammlung durchlaufen und eine Eigenschaft dieser Sammlung zurückgeben können:

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

Natürlich können Sie seit Version 3.0 von PowerShell einfach $ aufrufenservices.Name um die Namen aller Dienste zu erhalten, und das wird schneller abgeschlossen als die ForEach-Methode Alternative (obwohl Sie nur den Leistungsunterschied in sehr großen Sammlungen in der Größenordnung von Hunderttausenden von Objekten bemerken werden); dies funktioniert jedoch nur für Eigenschaften, die sich nicht in der Auflistung selbst befinden, und es handelt sich um eine Syntax, mit der einige Skripter aufgrund der impliziten Natur des Befehls nicht vertraut sind. Die neue Syntax der ForEach-Methode bietet Ihnen eine explizitere Alternative, die den zusätzlichen Vorteil hat, dass sie auch etwas selbstdokumentierender ist.

ForEach(string PropertyName, object newValue)

Sie können nicht nur eine Eigenschaft für eine Sammlung von Objekten abrufen, sondern auch eine Eigenschaft für eine Sammlung von Objekten festlegen. Dies ist eine Funktionalität, die in den anderen foreach nicht verfügbar ist, es sei denn, Sie erstellen den Skriptblock explizit dafür. Um die Eigenschaft festzulegen, geben Sie einfach den Eigenschaftsnamen und den Wert an, den Sie beim Festlegen dieser Eigenschaft verwenden möchten:

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

Genau wie bei Zuweisungen, die Sie mit dem Operator equals vornehmen würden, versucht PowerShell, alles, was Sie als neuen Wert angeben, in den entsprechenden Typ für die zugewiesene Eigenschaft umzuwandeln.

ForEach(string methodName) und ForEach(string methodName, Objektargumente)

Um eine Methode aufzurufen, geben Sie einfach den Methodennamen als erstes Argument und dann die Argumente für diese Methode als zweites, drittes, viertes usw. an. Argument. Wenn die Methode keine Argumente akzeptiert, können Sie einfach den Methodennamen übergeben und er wird ohne Argumente aufgerufen. Hier ist ein Beispiel, das zeigt, wie Sie eine Reihe von Prozessen beenden können, die ein bestimmtes Programm ausführen:

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

Hier ist ein weiteres Beispiel, das diesmal eine Methode mit Argumenten verwendet und zeigt, wie Sie überprüfen können, ob Befehle Best Practices befolgen, indem Sie geeignete Namen und Aliase für häufig verwendete Parameter verwenden:

# 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

Wie Sie diesen Ergebnissen entnehmen können, gibt es definitiv einige Inkonsistenzen bei der Implementierung von ComputerName-Parametern, die korrigiert werden sollten.

Das deckt alle Funktionen ab, die derzeit in der ForEach-Methode verfügbar sind. Wie Sie sehen können, gibt es in dieser Methode nicht viele neue Funktionen, aber die Syntaxverbesserungen, wenn Sie eine einfache Aufgabe für eine Sammlung von Objekten ausführen, sind nett, und die Leistungsverbesserungen der ForEach-Methode im Vergleich zur entsprechenden foreach-Anweisung für ForEach-Object Pipeline sind definitiv auch eine willkommene Verbesserung. Mit dieser Erklärung aus dem Weg, gehen wir zur Where-Methode über.

Die Where-Methode

Where ist eine Methode, mit der Sie eine Sammlung von Objekten filtern können. Dies ist dem Where-Object-Cmdlet sehr ähnlich, aber die Where-Methode ist auch wie Select-Object und Group-Object und enthält mehrere zusätzliche Funktionen, die das Where-Object-Cmdlet selbst nicht nativ unterstützt. Diese Methode bietet eine schnellere Leistung als Where-Object in einem einfachen, eleganten Befehl. Wie bei der ForEach-Methode werden alle Objekte, die von dieser Methode ausgegeben werden, in einer generischen Sammlung vom Typ System zurückgegeben.Sammlung.ObjectModel.Kollektion1.

Es gibt nur eine Version dieser Methode, die wie folgt beschrieben werden kann:

Where(scriptblock expression])

Wie in den eckigen Klammern angegeben, ist der Skriptblock expression erforderlich und die Modusaufzählung und das ganzzahlige Argument numberToReturn sind optional, sodass Sie diese Methode mit 1, 2 oder 3 Argumenten aufrufen können. Wenn Sie ein bestimmtes Argument verwenden möchten, müssen Sie alle Argumente links von diesem Argument angeben (dh wenn Sie einen Wert für numberToReturn angeben möchten, müssen Sie auch Werte für mode und expression angeben).

Where(scriptblock expression)

Der grundlegendste Aufruf der Where-Methode nimmt einfach einen Skriptblockausdruck als Argument. Der Skriptblockausdruck wird einmal für jedes Objekt in der verarbeiteten Auflistung ausgewertet, und wenn er true zurückgibt, wird das Objekt von der Where-Methode zurückgegeben. Dies ist das funktionale Äquivalent zum Aufrufen des Cmdlets Where-Object und zum Übergeben eines Skriptblocks. Wie das Cmdlet Where-Object können die Variablen $_ und $PSItem verwendet werden, um auf das aktuelle Element zu verweisen, das innerhalb des Skriptblocks verarbeitet wird.

Hier ist ein sehr einfaches Beispiel, das zeigt, wie Sie eine Liste der laufenden Dienste erhalten können.

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

Dies bietet keine neuen Funktionen, aber es bietet eine viel schnellere Leistung als Where-Object und die Syntax ist recht einfach zu befolgen.

Where(scriptblock-Ausdruck, WhereOperatorSelectionMode-Modus)

Wenn Sie sich die optionalen Parameter für die Where-Methode ansehen, werden die Dinge viel interessanter. Windows PowerShell Version 4.0 enthielt eine neue Enumeration mit dem Typnamen System.Management.Automatisierung.WhereOperatorSelectionMode. Beachten Sie das Suffix dieses Typnamens: „SelectionMode“. Es wird verwendet, um leistungsstarke Auswahlfunktionen in einer Where-Syntax bereitzustellen. Hier sind die in dieser Aufzählung enthaltenen Werte zusammen mit ihren Definitionen:

Standard Filtern Sie die Sammlung mithilfe des Ausdrucksskriptblocks auf eine maximale Anzahl, wenn eines angegeben wurde, oder standardmäßig auf alle Objekte in der Sammlung, wenn in der Sammlung keine maximale Anzahl angegeben wurde numberToReturn.
First Gibt die ersten N Objekte zurück, die den Blockfilter des Ausdrucksskripts übergeben, standardmäßig nur 1 Objekt, wenn in numberToReturn keine bestimmte Anzahl angefordert wurde.
Last Gibt die letzten N Objekte zurück, die den Blockfilter des Ausdrucksskripts übergeben, standardmäßig nur 1 Objekt, wenn in numberToReturn keine bestimmte Anzahl angefordert wurde.
SkipUntil Überspringen Sie Objekte in der Auflistung, bis ein Objekt den Blockfilter des Ausdrucksskripts passiert, und geben Sie dann die ersten N Objekte zurück, wobei standardmäßig alle verbleibenden Objekte verwendet werden, wenn in numberToReturn keine maximale Anzahl angegeben wurde.
Until Gibt die ersten N Objekte in einer Auflistung zurück, bis ein Objekt den Blockfilter des Ausdrucksskripts passiert, wobei standardmäßig alle Objekte verwendet werden, die zum ersten Objekt führen, das übergeben wurde, wenn in numberToReturn keine maximale Anzahl angegeben wurde.
Split Teilen Sie eine Sammlung in zwei Teile, indem Sie alle Objekte, die den Ausdrucksskriptblockfilter übergeben, bis zu einer maximalen Anzahl in die erste Sammlung einfügen, wenn in numberToReturn eines angegeben wurde, oder alle Objekte, die übergeben werden, wenn keine maximale Anzahl angegeben wurde, und alle anderen Objekte, die nicht in der ersten Sammlung enthalten sind, in die zweite Sammlung einfügen.

Jedes dieser Elemente bietet einen eindeutigen Wert, wenn Sie Datensammlungen verarbeiten.

Default

Es überrascht nicht, dass der Standardwert des mode-Arguments ‚Default‘ ist. Der Standardauswahlmodus bietet die gleiche Funktionalität, die Sie erhalten, wenn Sie überhaupt keinen Auswahlmodus bereitstellen. Zum Beispiel hätten wir die letzte Zeile unseres vorherigen Beispiels wie folgt schreiben können:

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

In diesem Beispiel ist das zusätzliche Argument jedoch nicht erforderlich, da es genau dasselbe tut, was es tun würde, wenn Sie das Argument nicht angeben würden. Sie können auch die maximale Anzahl von Objekten angeben, die Sie im Standardauswahlmodus zurückgeben möchten, indem Sie das Argument numberToReturn wie folgt verwenden:

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

Es ist wichtig zu beachten, dass die genaue Funktionalität auch bei Verwendung des ersten Auswahlmodus verfügbar ist (über den wir gleich sprechen werden), daher ist es wirklich nicht praktikabel, einen der optionalen Parameter überhaupt zu verwenden, wenn Sie den Standardauswahlmodus verwenden.

First

Wie Sie vielleicht erraten haben, können Sie im ersten Auswahlmodus die ersten Objekte in der Sammlung auswählen, die den Skriptblockausdrucksfilter übergeben. Wenn Sie First ohne Wert für das Argument numberToReturn oder First mit dem Wert 0 für das Argument numberToReturn verwenden, wird nur das erste Objekt zurückgegeben, das den Filter passiert. In diesem Fall werden so viele Objekte zurückgegeben (vorausgesetzt, es gibt so viele Objekte, die den Filter passieren).

Hier sind einige Beispiele mit unserer Services-Sammlung, die den ersten Auswahlmodus in Aktion zeigen:

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

Beachten Sie, dass der zweite Befehl in diesen Beispielen die gleichen Ergebnisse wie der erste Befehl zurückgibt, da er einfach explizit den Standardwert des numberToReturn-Arguments übergibt, wenn der Erste Auswahlmodus verwendet wird.

Last

Der letzte Auswahlmodus funktioniert ähnlich wie der erste Auswahlmodus, sodass Sie die letzten Objekte in der Sammlung auswählen können, die den Skriptblock-Ausdrucksfilter übergeben. Wenn Sie Last ohne Wert für das Argument numberToReturn oder Last mit dem Wert 0 für das Argument numberToReturn verwenden, wird nur das letzte Objekt zurückgegeben, das den Filter passiert. In diesem Fall werden so viele Objekte zurückgegeben (vorausgesetzt, es gibt so viele Objekte, die den Filter passieren).

Hier sind einige Beispiele mit unserer Services-Sammlung, die den letzten Auswahlmodus in Aktion zeigen:

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

Wie in den Beispielen für den ersten Auswahlmodus gibt auch der zweite Befehl in diesen Beispielen dieselben Ergebnisse zurück wie der erste Befehl, da er einfach explizit den Standardwert des numberToReturn-Arguments übergibt, wenn der letzte Auswahlmodus verwendet wird.

SkipUntil

Mit dem Auswahlmodus SkipUntil können Sie alle Objekte in einer Sammlung überspringen, bis Sie eines finden, das den Skriptblock-Ausdrucksfilter passiert. Sobald Sie ein Objekt gefunden haben, das den Filter passiert, gibt der SkipUntil-Modus entweder alle in der Auflistung verbleibenden Objekte zurück, wenn für das Argument numberToReturn kein Wert oder ein Wert von 0 angegeben wurde, oder er gibt die ersten N verbleibenden Objekte in der Auflistung zurück, wenn für das Argument numberToReturn ein Wert größer als Null angegeben wurde. In beiden Fällen enthalten die Ergebnisse das erste Objekt, das den Filter bestanden hat.

Hier sind einige Beispiele, die eine Teilmenge unserer Services-Sammlung verwenden, um den SkipUntil-Auswahlmodus in Aktion zu zeigen:

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

Until

Der Auswahlmodus Until bietet die entgegengesetzte Funktionalität des Auswahlmodus SkipUntil. Sie können Objekte in einer Sammlung zurückgeben, bis Sie eines gefunden haben, das den Skriptblockausdrucksfilter übergibt. Sobald Sie ein Objekt gefunden haben, das den Filter passiert, stoppt die Where-Methode die Verarbeitung von Objekten. Wenn Sie keinen Wert für das Argument numberToReturn oder den Wert 0 angeben, gibt der Auswahlmodus Until alle Objekte in der Auflistung zurück, die zu dem ersten Objekt führen, das den Skriptblockausdrucksfilter übergibt. Wenn Sie einen Wert für das Argument numberToReturn angeben, der größer als 0 ist, gibt der Auswahlmodus Bis höchstens diese Anzahl von Objekten zurück, was bedeutet, dass möglicherweise nicht einmal ein Objekt gefunden wird, das den Skriptblockausdrucksfilter übergibt.

Hier sind einige Beispiele, die eine andere Teilmenge unserer Services-Sammlung verwenden, um den Split-Auswahlmodus in Aktion zu zeigen:

# 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

Der Split-Auswahlmodus ist einzigartig. Anstatt eine Teilmenge der Sammlung zurückzugeben, mit der Sie in einer neuen Sammlung beginnen, wird eine neue Sammlung zurückgegeben, die intern zwei separate Sammlungen enthält. Was diese verschachtelten Sammlungen enthalten, hängt davon ab, wie Sie den geteilten Auswahlmodus verwenden. Split ermöglicht es Ihnen, eine Sammlung von Objekten in zwei Teile zu teilen. Wenn Sie keinen Wert für das Argument numberToReturn oder den Wert 0 für das Argument numberToReturn angeben, platziert Split standardmäßig alle Objekte, die den Skriptblockausdrucksfilter übergeben, in der ersten verschachtelten Auflistung und alle anderen Objekte (diejenigen, die den Skriptblockausdrucksfilter nicht übergeben) in der zweiten verschachtelten Auflistung. Wenn Sie für das Argument numberToReturn einen Wert größer als 0 angeben, begrenzt split die Größe der ersten Auflistung auf diesen Maximalbetrag, und alle verbleibenden Objekte in der Auflistung, auch diejenigen, die dem Skriptblockausdrucksfilter entsprechen, werden in der zweiten Auflistung platziert.

Hier sind einige Beispiele, die zeigen, wie der Split-Auswahlmodus verwendet werden kann, um eine Sammlung von Objekten auf verschiedene Arten aufzuteilen:

# 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

Wie Sie in diesem Beispiel sehen können, ist Split ein ziemlich leistungsfähiger Auswahlmodus, der eine Mischung aus Filtern, Gruppieren und Auswählen in einem einzigen Befehlsaufruf bietet.

Diese Sammlung von Auswahlmodi macht die Where-Methode schnell und elegant und gleichzeitig leistungsfähiger als die Where-Object , Group-Object und Select-Object in einer einzigen Pipeline. Was ist daran nicht zu lieben?

Mängel in den ForEach- und Where-Skriptmethoden in TypePx

Wie ich bereits in diesem Artikel erwähnt habe, habe ich Typerweiterungen für PowerShell 3 geschrieben.0 und später und verpackte sie in ein Modul namens TypePx. TypePx ist ein Skriptmodul, das vollständig in PowerShell geschrieben wurde und auf PowerShell 3.0 oder höher ausgeführt wird. Wenn Sie PowerShell 3.0 verwenden (und nur, wenn Sie PowerShell 3.0 verwenden), definiert TypePx ForEach- und Where-Skriptmethoden, die das Verhalten der ForEach- und Where-Methoden in PowerShell 4.0 und höher nachahmen. Während das erweiterte Typsystem in PowerShell es ermöglicht, dieses Verhalten nachzuahmen, gibt es einige Mängel aufgrund der Implementierung in PowerShell, die sich darauf auswirkten, wie weit ich mit diesen Typerweiterungen gehen konnte. In diesem Abschnitt werden einige der Unterschiede und Einschränkungen beschrieben, die in den ForEach- und Where-Skriptmethoden in TypePx bestehen, die Sie möglicherweise beachten sollten, wenn Sie PowerShell 3.0 verwenden.

Skriptblöcke, die von einer Skriptmethode aufgerufen werden, werden in einem untergeordneten Bereich ausgeführt

Im Gegensatz zu den ForEach- und Where-Methoden, die als Teil von PowerShell 4 implementiert wurden.0 oder höher, die den Ausdrucksskriptblock im aktuellen Bereich aufrufen, in dem die Methode aufgerufen wird, die in PowerShell 3.0 implementierten ForEach- und Where-Skriptmethoden rufen den Ausdrucksskriptblock in einem untergeordneten Bereich auf. Dies ist eine Einschränkung in PowerShell, die von Anfang an vorhanden war (eine Einschränkung, die meiner Meinung nach bei weitem das größte Manko von PowerShell als Sprache ist).

Aufgrund dieser Einschränkung werden alle Variablen, die Sie innerhalb eines Ausdrucksskriptblocks zuweisen, nur im untergeordneten Bereich geändert. Dies hat Auswirkungen, wenn Ihr Ausdrucksskriptblock eine Variable in dem Bereich aktualisieren soll, in dem Sie ForEach oder Where aufrufen. Es ist unwahrscheinlich, dass dies bei der Verwendung von Where zu einem Problem führen würde, da es nicht sehr häufig ist, Variablen in einem Where-Ausdrucksskriptblock zu ändern, aber in ForEach-Skriptblöcken kann dies ein Problem darstellen.

Ich sollte beachten, dass ich diese Einschränkung ganz entfernen möchte, und ich glaube, dass ich in der Lage sein werde, dies zu tun, aber zum Zeitpunkt, als ich diesen Artikel schrieb, habe ich noch keinen Fix dafür implementiert.

Die meisten, aber nicht alle Sammlungen verfügen über ForEach- und Where-Methoden

In PowerShell 4.0 und höher werden die ForEach- und Where-Methoden auf magische Weise für alle Typen verfügbar gemacht, die IEnumerable implementieren, mit Ausnahme von String, XmlNode und Typen, die IDictionary implementieren. In PowerShell können mit dem erweiterten Typsystem keine Erweiterungen für Schnittstellen, sondern nur für Typen erstellt werden. Dies ist eine Herausforderung, wenn Sie eine breite Erweiterung wie ForEach und Where erstellen möchten. In der aktuellen Implementierung dieser Erweiterungen in TypePx findet das TypePx-Modul alle Typen in allen Assemblys, die in der aktuellen App-Domäne geladen wurden, und für alle nicht generischen Typen, die IEnumerable, aber nicht IDictionary definieren (mit Ausnahme von String und XmlNode), sowie für alle generischen Typen, die IEnumerable , aber nicht IDictionary für generische Sammlungen von PSObject , Object, String, Int32 oder Int64 definieren, die Skriptmethoden ForEach und Where werden erstellt.

Dies deckt eine große Anzahl von Typen ab, die in meinen eigenen Tests ausreichend waren, aber Sie können auf Typen stoßen, bei denen Sie diese Methoden verwenden möchten und die nicht verfügbar sind. Wenn dies der Fall ist, lassen Sie es mich über GitHub wissen und ich werde sehen, was ich tun kann. Dies ist auch eine Einschränkung, die ich entfernen möchte, aber ich benötige mehr Zeit, um zu untersuchen, wie die entsprechende Funktionalität in einer kompilierten Assembly implementiert werden kann, in der ich sie möglicherweise eher so definieren kann, wie sie in PowerShell 4.0 und höher definiert ist.

PowerShell-Skripte sind nicht so schnell wie kompilierter Code

Dies ist wahrscheinlich selbstverständlich, aber wenn Sie etwas in PowerShell schreiben, einer interpretierten Sprache, wird es nicht so schnell ausgeführt, wie wenn Sie die entsprechende Logik in einer Sprache wie C # schreiben würden. Dies ist absolut der Fall für die ForEach- und Where-Skriptmethoden, die intern ForEach-Object , Where-Object und Pipelining verwenden, um das Verhalten der nativen ForEach- und Where-Methoden nachzuahmen. In diesem Fall liegt der Vorteil dieser Befehle in der eleganten Syntax und Funktionalität, die sie bieten, sowie in der Möglichkeit, diese in Skripten für PowerShell 3.0 und 4.0 zu verwenden. Die Leistungsvorteile in ForEach und Where liegen nur in der nativen PowerShell 4.0-Implementierung.

PowerShell 3.0 erfordert Klammern um alle Methodenparameter

Ich habe in den obigen Beispielen erwähnt, dass ich eine Methode mit einem einzelnen literalen Skriptblockparameter aufrufen konnte, ohne diesen literalen Skriptblock in zusätzliche Klammern zu setzen. Diese Funktion ist nur in PowerShell 4 vorhanden.0 oder höher aufgrund von Verbesserungen, die in dieser Version am Parser vorgenommen wurden. In PowerShell 3.0 unterstützt der Parser dies nicht, daher sind Klammern immer erforderlich, damit die Skriptmethoden ForEach und Where mit einem einzelnen literalen Skriptblockparameter in dieser Version arbeiten können.

Fazit

Als ich anfing, das Verhalten der magischen Methoden ForEach und Where in PowerShell 3.0 nachzuahmen, war mir nicht ganz klar, wie viel Funktionalität sie bieten. Wenn ich mich mit den technischen Details hinter diesen Methoden befasse, damit ich die gewünschten Erweiterungen in TypePx erstellen kann, konnte ich alle versteckten Funktionen dieser sehr leistungsstarken Methoden aufdecken, und ich freue mich sehr, diese in diesem Artikel mit Ihnen zu teilen. Ich hoffe, diese Informationen helfen Ihnen, diese wunderbaren neuen Funktionen in Ihrer PowerShell-Arbeit zu nutzen, auch wenn Sie noch PowerShell 3.0 verwenden. Glückliche Scripting!