Richard Siddaway hat kürzlich einen Blog zu dem Thema veröffentlicht, den ich hier gerne nachvollziehen möchte. $_ und Where-Object werden einfach zu häufig benutzt, ohne das nötige Hintergrundwissen zu haben. Deshalb seht ein paar Beispiele und die Erklärung unten.
$_ ist das aktuelle Pipeline Objekt. Man kann auch $PSitem verwenden, jedoch ist $_ viel gebräuchlicher. Wenn also der vorherige Befehl (z.B. Get-Service) mehrere Objekte ausgibt dann ist $_ immer das aktuelle Objekt, eines nach dem anderen.
Wenn also das Ergebnis eines Befehls nachträglich gefiltert werden soll kann das so aussehen wie unten
[codesyntax lang=“powershell“]
1 2 3 4 |
# Logische Schreibweise die auch erklärt was passiert: Get-Service | Where-Object -FilterScript {$_.Name -like 'a*'} # bekanntere Schreibweise Get-Service | Where-Object {$_.Name -like 'a*'} |
[/codesyntax]
Ab PowerShell 3.0 gibt es eine einfacher Variante
[codesyntax lang=“powershell“]
1 2 3 |
Get-Service | Where Name -like 'a*' #was eine verkürzte Version des Befehls unten darstellt. Get-Service | Where -Property Name -like -Value 'a*' |
[/codesyntax]
Das ist effizient, hat aber den Haken, dass keine logischen Verknüpfungen (-and –or) verwendet werden können. Wenn also mehrere Filter angewendet werden sollen muss man zur “alten” Schreibweise greifen. Dies funktioniert mit und ohne runde Klammern.
[codesyntax lang=“powershell“]
1 2 |
Get-Service | Where-Object {($_.Name -like 'a*') -and ($_.Status -eq 'Stopped')} Get-Service | Where-Object {$_.Name -like 'a*' -and $_.Status -eq 'Stopped'} |
[/codesyntax]
$_ kann man natürlich auch mit Foreach verwendet. Das Beispiel unten liest die Netzwerkadapter mittels CIM-Cmdlets aus, speichert bestimmte Eigenschaften des CIM Objektes in Variablen und in Folge in einer HashTabelle. Zum Schluss wird das Ganze dann in einem PSObject abgelegt und ausgegeben.
[codesyntax lang=“powershell“]
1 2 3 4 5 6 7 8 9 10 11 |
Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetEnabled = $true" | ForEach-Object { $ip = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index = $($_.DeviceId)" $props = @{ Name = $_.NetConnectionId Product = $_.ProductName DHCP = $ip.DHCPEnabled IP = $ip.IPAddress } New-Object -TypeName PSObject -Property $props } |
[/codesyntax]
Unten bringt Rick noch ein Beispiel mit Berechnungen der Diskgröße, welches wir hier mittels $PSItem statt $_ verwenden nur um zu zeigen das es egal ist welche Variable ihr verwendet.
[codesyntax lang=“powershell“]
1 2 3 4 5 |
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceId, VolumeName, @{N='Size(GB)'; E={[math]::Round($PSItem.Size / 1GB, 2)}}, @{N='Used(GB)'; E={[math]::Round(($PSItem.Size - $_.FreeSpace) / 1GB, 2)}}, @{N='Free(%)'; E={[math]::Round(($PSItem.FreeSpace / $_.Size) * 100, 2 )}} |
[/codesyntax]
$PSItem.Site und $_FreeSpace werden hier gemischt, funktionieren also auch “gemischt”
Was nicht funktioniert, ist die Verwendung von $_ in Foreach Schleifen. Den genauen Unterschied zwischen foreach und foreach-object kann man hier nachlesen). Also, der Code unten wird nicht funktionieren.
[codesyntax lang=“powershell“]
1 2 3 4 5 6 7 8 9 10 11 12 |
$nics = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetEnabled = $true" $data = foreach ($nic in $nics) { $ip = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index = $($_.DeviceId)" $props = @{ Name = $_.NetConnectionId Product = $_.ProductName DHCP = $ip.DHCPEnabled IP = $ip.IPAddress } New-Object -TypeName PSObject -Property $props } $data |Format-List |
[/codesyntax]
So, und nun viel Spass beim ausprobieren !
R.