Wenn man nicht vertikal (nach Spalten) filtern möchte, sondern ganze Objekte aus der Pipeline entfernen will, verwendet man Where-Object, der an die SQL Where-Klausel angelehnt ist. Where-Object benötigt ein Filter-Skript, um zu bewerten, ob ein Objekt aus der Pipeline entfernt werden soll. Die einfachste Form eines Where-Object sieht so aus:
Get-Service | Where-Object -FilterScript { $true }
Das Filterskript, das über den Parameter Filterscript übergeben wird, ist das Kommando, das ausgeführt wird, um zu bestimmen, ob ein Objekt in der Pipeline weitergeleitet werden soll. Es wird als Skriptblock (s. Abschnitt Skriptblöcke) angegeben. Skriptblöcke sind eigenständige Skripte, die einen "Starter" benötigen, also jemanden, der die Ausführung des Skriptblocks einleitet – in diesem Fall übernimmt Where-Object diese Aufgabe. Im Prinzip ist ein Skriptblock einfach nur ein Behälter, um Code zu kapseln. Der Beginn und das Ende des Skriptblocks sind durch die geschweiften Klammern markiert.
Where-Object führt das Filterskript für jedes Objekt in der Pipeline einmal aus und prüft dann das Ergebnis. Liefert der Skriptblock "Falsch" oder gar nichts zurück, wird das Objekt aus der Pipeline entfernt. Falsch wird dabei durch die Zahl 0 repräsentiert, kann aber auch über die Variable $false angegeben werden. $false ist eine von Powershell bereitgestellt Variable (genau genommen eine Konstante), die einfach die Zahl 0 beinhaltet. "Nichts" kann ein leerer String sein – also Anführungszeichen ohne Inhalt, wie "" – oder der Inhalt der Variablen $null. Jeder (!) andere Rückgabewert wird von Powershell als wahr interpretiert. Dieses Konzept wird auch als Truthy und Falsy bezeichnet, weil es faktisch nicht wahr und falsch auseinanderhält, sondern nur auf falsch prüft.
|
Rückgabe Skriptblock
|
Verhalten von Where-Object
|
|
0
|
Entfernen des Objekts
|
|
$false
|
Entfernen des Objekts
|
|
""
|
Entfernen des Objekts
|
|
$null
|
Entfernen des Objekts
|
|
Jede andere Rückgabe
|
Weiterleiten des Objekts
|
Im Beispiel gibt das Filterskript für jedes Objekt den Inhalt der Standardvariable $true zurück. Da $true nicht den oben genannten Ausschluss-Kriterien entspricht, werden alle Objekte weitergeleitet. Geben Sie stattdessen im Filterskript $false an, wird gar nichts mehr zurückgegeben, da Where-Object alle Objekte aus der Pipeline entfernt.
Get-Service | Where-Object -FilterScript { $false }
Diese Beispiele sind ziemlich sinnlos, zeigen aber sehr schön, wie Where-Object funktioniert. Natürlich kann man auch komplexere Überprüfungen ausführen, da im Filterskript ja beliebiger Code angegeben werden kann. So kann man beispielsweise prüfen, ob ein Dienst sich im Status "Stopped" befindet, indem man die Objekt-Eigenschaft Status im Filterskript untersucht.
Um zwei Werte miteinander vergleichen zu können, benötigen wir Vergleichsoperatoren – Sie kennen Vergleichsoperatoren aus dem Schulunterricht: = (gleich), < (kleiner als), > (größer als), <> (ungleich) usw. Man spricht von Boolschen Operatoren, ihre Rückgabe kann per Definition nur wahr oder falsch sein. Der Begriff geht übrigens auf den englischen Mathematiker George Boole zurück, der diese Operationen entwickelt hat.
Bei Powershell werden die Vergleichsoperatoren anders als in der Mathematik mit Sprach-Abkürzungen angeben, da die Symbole = und > bereits anderweitig belegt sind. Die Powershell-Operatoren sind der Linux Shell entlehnt, wie auch viele andere Konzepte. Hier eine unvollständige Auflistung der wichtigsten Operatoren:
|
Vergleichsoperator
|
Mathematischer / SQL-Operator
|
Bedeutung
|
|
-eq
|
=
|
equal
|
|
-neq
|
<> oder !=
|
not equal
|
|
-lt
|
<
|
Less than
|
|
-gt
|
>
|
Greater than
|
|
-le
|
<=
|
Less or equal
|
|
-ge
|
>=
|
Greater or equal
|
|
-like
|
Like (in SQL)
|
Wie equal mit Platzhaltern (*,?)
|
|
-notlike
|
Notlike (in SQL)
|
Wie not equal mit *,?
|
|
-match
|
-
|
Wie like mit komplexen Mustern (s.
Reguläre Ausdrücke, S. 122)
|
|
-contains
|
In (SQL)
|
Suchbegriff in Array?
|
|
-notcontains
|
Notin (SQL)
|
Suchbegriff nicht in Array?
|
|
-in
|
In (SQL)
|
Array enthält Suchbegriff?
|
|
-notin
|
NotIn (SQL)
|
Array enthält Suchbegriff nicht?
|
|
-not / !
|
! / Not (SQL)
|
Negierung, dreht Ergebnis um
|
Um das Objekt aus der Pipeline im Filterskript zu verwenden, muss man Where-Object anzeigen, wo im Filterskript das Objekt vor der Ausführung eingetragen werden soll. Da es sich, je nachdem, was man in der Pipeline verarbeitet, um beliebige Typen von Objekten handeln kann, verwendet man einen universellen Platzhalter, die "anonyme" Variable, dargestellt durch $_:
Get-Service | Where-Object -FilterScript { $_.Status -eq 'stopped' }
$_ ist ein Platzhalter für die einzelnen Dienstobjekte, die sich in der Pipeline befinden. Where-Objekt ersetzt $_ durch den Dienst, der jeweils von der Pipeline verbeitet wird. $_.Status bedeutet also, dass an dieser Stelle die Eigenschaft Status des Dienstes eingetragen werden soll. Da Boolsche Operatoren immer nur wahr oder falsch zurück liefern, gibt das Filterskript für jeden einzelnen Dienst "True" zurück, wenn der Dienst gestoppt ist, und in allen anderen Fällen "False".
Wenn Sie auf größer oder kleiner filtern wollen, verwenden Sie die Operatoren -gt bzw. -lt:
Get-ChildItem -Path C:\Windows\ | Where-Object -FilterScript { $_.Length -gt 1MB }
> Verzeichnis: C:\Windows
> Mode LastWriteTime Length Name
> ---- ------------- ------ ----
> -a---- 23.05.2020 17:26 4483160 explorer.exe
> -a---- 07.12.2019 10:09 1075200 HelpPane.exe
> -a---- 01.05.2019 16:37 2839520 RtlExUpd.dll
> -a---- 06.10.2019 20:43 1464464 Sysmon.exe
Get-WinEvent -ListLog * | Where-Object { $_.MaximumSizeInBytes -gt 128MB }
> LogMode MaximumSizeInBytes RecordCount LogName
> ------- ------------------ ----------- -------
> Retain 1048985600 0 Microsoft-Windows-PowerShell/Admin
Das Cmdlet Get-WinEvent listet wie Get-Eventlog die Inhalte von Eventlogs aus, kann allerdings auch die neue in Windows Vista hinzugekommen Applikations-Logs verarbeiten.
Get-Eventlog vs. Get-WinEvent
Get-Eventlog listet nur die klassischen Eventlogs auf und wurde bereits mit Powershell 1 eingeführt. Get-Winevent hat es unter Powershell 7 vollständig abgelöst - das alte Get-Eventlog ist in Powershell 7 also gar nicht mehr verfügbar. Unter Powershell 5 benutze ich es aber trotzdem nach wie vor hilfreich, wenn man die klassischen Eventlogs filtern möchte, da es deutlich einfacher zu bedienen ist als Get-WinEvent.
Wenn Sie einen Vergleich mit Wildcards (einfachen Platzhalten) durchführen wollen, verwenden Sie den Operator -Like:
Get-Process | Where-Object { $_.Path -like '*\WindowsPowershell\*' }
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
816 52 59440 84740 7,61 15144 1 powershell
Hier sehen Sie zwei Besonderheiten – zum einen ist -Filterscript ein Positionsparameter, kann also weggelassen werden, zum anderen sind hier zwei Platzhalter verwendet worden, um sich alle laufenden Programme anzeigen zu lassen, die sich in einem beliebigen Ordner mit dem Namen WindowsPowershell befinden.
Ein häufiger Fehler ist, Wildcards mit dem Operator -eq zu verwenden:
Get-HotFix | Where-Object { $_.HotFixID -eq 'KB40*' }
Dieser Filter liefert nichts zurück, da das * nicht als Platzhalter interpretiert wird, sondern als das Zeichen *. Bei der Verwendung von Platzhaltern benötigen Sie immer den -Like Operator.
Get-Hotfix liefert Ihnen übrigens die installieren Updates zurück – warum das Cmdlet Get-Hotfix heißt, weiß nur der liebe Gott und vielleicht Jeffrey Snover…
Mit den Operatoren -and und -or können Sie mehrere Bedingungen miteinander verknüpfen:
Get-ChildItem -Path $Home -Recurse |
Where-Object { $_.name -like '*~*' -and $_.Length -gt 1MB }
> Verzeichnis: C:\Users\Holger\.vscode\extensions\ms-python.python-2020.5.78807\out\datascience-ui\notebook
> Mode LastWriteTime Length Name
> ---- ------------- ------ ----
> -a---- 13.05.2020 15:58 1954472 vendors~dataresource.bundle.js
> -a---- 13.05.2020 15:58 3283186 vendors~plotly.bundle.js
> -a---- 13.05.2020 15:58 1759298 vendors~vega.bundle.js
Dieses Beispiel sucht nach allen Dateien im Homeverzeichnis des Benutzers, die eine Tilde ~ im Namen tragen – ein deutliches Zeichen für eine temporäre Datei - und größer als 1 MB sind.
Wenn Sie eine Eigenschaft auf mehrere Bedingungen prüfen wollen, wie z.B. auf unterschiedliche Dateiendungen, können Sie den Operator -in verwenden:
Get-ChildItem -Path C:\Windows\System32 |
Where-Object { $_.Extension -in '.exe','.dll' }
-in erwartet auf der linken Seite die zu untersuchende Eigenschaft, und auf der rechten Seite ein Array von Vergleichswerten. Die Eigenschaft muss genau einem der Werte in der Liste entsprechen, damit der Vergleich True ergibt. Der Operator -contains dreht einfach nur die Reihenfolge um: Vergleichsarray links, Eigenschaft rechts:
Get-ChildItem -Path C:\Windows\System32 |
Where-Object { '.exe','.dll' -contains $_.Extension }
Der Operator -In lässt sich zwar einfacher merken, da er die "natürliche" Reihenfolge
$_.Eigenschaft <Vergleichsoperator> $Referenzwert
beibehält, aber er wurde erst mit Powershell 3 eingeführt. Muss Ihr Skript auch noch unter Powershell 2 laufen, benutzen Sie -Contains. -In und -Contains lassen sich auch wunderbar verwenden, um eine Liste von Werten gegen ein Array zu filtern:
$ExcludeAccounts = 'Krbtgt','guest','DefaultAccount','administrator'
Get-ADUser -Filter * | Where-Object { $_.Name -notin $ExcludeAccounts }
Für dieses Beispiel benötigen Sie eine Domäne und das Powershell-AD-Modul installiert. Der Befehl gibt alle Domänenbenutzer zurück außer den Standard-konten, die für die AD-Verwaltung verwendet werden.
Ab Powershell 3 gibt es eine vereinfachte Schreibweise des Where-Object:
Get-ScheduledTask | Where-Object state -eq running
Bei dieser Schreibweise wird kein Filterskript übergeben. Tatsächlich ist diese Variante implementiert worden, indem für jeden verfügbaren Vergleichsoperator ein Parameter zu Where-Object hinzugefügt wurde, der das linke und das rechte Argument als Positionsparameter einliest und dann intern wieder ein Filterskript baut und ausführt. Man sieht das sehr schön, wenn man sich die Syntax der Hilfe zu Where-Object ansieht:

Abbildung 4 - where-Object hat sehr viele Parameter-Sätze
Verpflichtend (mandartory) ist der Vergleichsoperator, die links und rechts übergebenen Argumente werden als Positionsparameter eingelesen, und haben die Parameternamen -Property und -Value. Man kann die verkürzte Form also auch so schreiben:
Get-ScheduledTask | Where-Object -Property state -eq -value Running
Grundsätzlich ist die vereinfachte Schreibweise schneller getippt, aber sie erlaubt keine Verknüpfung von mehreren Bedingungen, da man -or und -and nicht verwenden kann. Meine Empfehlung: Verwenden Sie in Skripten weiterhin die alte Schreibweise, in der Konsole können Sie, wenn es schnell gehen soll, ruhig die vereinfachte Schreibweise verwenden.
Eine noch kürzere Schreibweise von Where-Object sieht so aus:
Get-ScheduledTask | ? State -eq Running
Das Fragezeichen ist ein Alias und steht ebenfalls für Where-Object. Hier gilt das Eben gesagte: Für Skripte sollten Sie diese Schreibweise meiden, weil sie schwer zu lesen ist, in der Konsole tippt sich ? aber schneller als Where-Object.
Eine weitere Besonderheit von Where-Object ist, dass er das Objekt in der Pipeline auch dann weiterleitet, wenn er statt $true irgendeinen Rückgabewert bekommt (s.o.):
"Ich gehe durch Where-Object" | Where-Object -Filterscript { $_ }
> Ich gehe durch Where-Object
"Ich gehe durch Where-Object" wird vom Filterscript ausgeben und von Where-Object nicht herausgefiltert, da das Ergebnis weder falsch noch leer ist. Das Skript gibt den Text also direkt wieder aus.
Das ist ausgesprochen nützlich, denn mit dieser Funktionalität kann man z.B. Objekte ausfiltern, die leere Eigenschaften haben:
Get-Partition | Where-Object { $_.GptType }
Get-Partition | Where-Object { -not $_.DriveLetter }
Im ersten Beispiel werden alle Partitionen angezeigt, bei denen die Eigenschaft GptType gesetzt ist, was letztlich bedeutet, dass alle Datenträger ausgefiltert werden, die als MBR-Datenträger initialisierte wurden. Im zweiten Beispiel wird -not verwendet, um die Ausgabe zu negieren. Es werden also alle Partitionen angezeigt, die keinen (!) Laufwerksbuchstaben besitzen.
Eine vollständige Auflistung aller Vergleichsoperatoren finden Sie in der Powershell-Hilfe mit Get-Help About_comparison_operators oder direkt in der Online-Hilfe.