Private/Wissen/B_Basic/B53_Objekt-Handling.ps1
<#
# Objekt-Handling Der richtige Umgang mit Objekten in der PowerShell. - **Hashtags** Object Where Select Sort Group Measure Compare ForEach New Analyse static inherits - **Version** 2020.02.29 #> # READ Weiterführende und Nachschlage-Informationen: Get-Help -Name 'about_Objects' -ShowWindow Get-Help -Name 'about_Object_Creation' -ShowWindow Get-Help -Name 'about_Pipelines' -ShowWindow Get-Command -Noun 'Object' -Module 'Microsoft.PowerShell.*' #region Objekte-Analyse # ! siehe .\AKPT\Private\Wissen\B_Basic\B52_Objekt-Analyse.md #endregion #region Mit den Default-Cmdlets (*-Object) Rückgabe-Objekte zu managen # ? Eine Übersicht von Cmdlets die Rückgabe-Objekte managen können: Get-Command -Name Get-Command -Noun Object -Module Microsoft.PowerShell.* Get-Command -Name Where-Object -Syntax # Filtern (Zeile) Get-Command -Name Select-Object -Syntax # Filtern (Spalten, First, Last, ExpandProperty, ...) Get-Command -Name Sort-Object -Syntax # Sortieren Get-Command -Name Group-Object -Syntax # Gruppieren Get-Command -Name Measure-Object -Syntax # Messen (Count, Sum, Min, Max, Avg) Get-Command -Name Compare-Object -Syntax # Vergleichen Get-Command -Name ForEach-Object -Syntax # Schleife Get-Command -Name New-Object -Syntax # Erstellen Get-Command -Name Tee-Object -Syntax # Verzweigen & loggen #region Sort-Object Get-Process | Sort-Object -Property 'Name' Get-Process | Sort-Object -Property 'WorkingSet64' -Descending Get-Process | Sort-Object -Property 'WorkingSet64' -Descending | Select-Object -First 3 #endregion #region Select-Object Get-Process | Select-Object -Property 'Name', 'WorkingSet64' # ALLE anderen Eigenschaften werden entfernt und spart Speicherplatz! Get-Process | Select-Object -Skip 3 -First 2 # Der 4. und 5. Prozess wird zurück gegeben Get-Process | Select-Object -SkipLast 5 # Alle außer die letzten 5 Get-Process | Select-Object -Last 2 Get-Process | Select-Object -Index 5 # 0-Basierend; Identisch mit folgendem Aufruf: (Get-Process)[5] Get-Process | Select-Object -Property 'Name' -Unique Get-Process | Select-Object -ExpandProperty 'Name' -First 2 ; <# vs.#> ; Get-Process | Select-Object -Property 'Name' -First 2 #endregion #region Measure-Object Get-Process | Measure-Object Get-Process | Measure-Object -Property 'WorkingSet64' -Sum -Average -Minimum -Maximum #endregion #region Where-Object # Einfache Schreibweise Get-Command -Name 'Where-Object' -Syntax Get-Process | Where-Object -Property 'Company' -Like -Value 'Microsoft*' Get-Process | Where-Object 'Company' -Like 'Microsoft*' Get-Process | Where-Object -Like 'Company' 'Microsoft*' Get-Process | Where-Object 'Microsoft*' -Like 'Company' Get-Process | Where-Object -Like 'Microsoft*' 'Company' # -or | -and: Ausführliche Schreibweise seit PS1.0 Get-Process | Where-Object -FilterScript { $_.Company -like 'Microsoft*' -or $_.Company -like 'Oracle*' } # ! Die Variable {$_} ist die Laufzeit-Variable in der Pipeline und stellt das übergebene Pipeline-Objekt dar! Get-Process -PipelineVariable 'prozess' | Where-Object -FilterScript { $prozess.Company -like 'Microsoft*' -or $prozess.Company -like 'Oracle*' } #endregion #region Group-Object (Gruppiert zu Objektgruppen) Get-Service | Group-Object -Property 'Status' Get-ChildItem -Path 'C:\Windows' -File -Force | Group-Object -Property 'Extension' | Sort-Object -Property 'Count' -Descending | Select-Object -First 3 #endregion #region Compare-Object Start-Process -FilePath 'calc' $x = Get-Process Stop-Process -Name '*Calc*' Start-Process -FilePath 'notepad.exe' $y = Get-Process Compare-Object -ReferenceObject $x -DifferenceObject $y -Property 'Name' Compare-Object -DifferenceObject $y -ReferenceObject $x -Property 'Name' # TIPP: Doppelte Dateien finden s. Get-FileHash Compare-Object -ReferenceObject $x -DifferenceObject $y -ExcludeDifferent -IncludeEqual #endregion #region New-Object Add-Type -AssemblyName 'System.Windows.Forms' $o1 = New-Object -TypeName 'System.Windows.Forms.Form' # Erstellt ein Objekt aus der .NET-Framework-Klasse 'Form' $o1.ShowDialog() # .NET Wissen $o2 = New-Object -ComObject 'Excel.Application' $mappe = $o2.Workbooks.Add() $sheet = $mappe.Worksheets.Add() $cellA1 = $sheet.Range('A1') $cellA1.Value2 = 'Greetings from PowerShell' $o2.Visible = $true # VBA for Application # TIPP Besser per ImportExcel-Modul .XLSX-Dateien erzeugen! #endregion #region ForEach-Object 31, 32, 33, 34, 35, 36 | ForEach-Object -Process { "192.168.178.$_" } 31..36 | % { "192.168.178.$_" } # ? Eine Übersicht aus Name, Größe und Besitzer von c:\Temp: Get-ChildItem -Path 'C:\Temp' -File | Get-Member # ! Kein Datei-Owner vorhanden. Get-ChildItem -Path 'C:\Temp' -File | Get-Acl | Get-Member # ! Keine Datei-Length vorhanden. # ? LÖSUNG: Get-ChildItem -Path 'C:\Temp' -File | ForEach-Object -Process { $dateiname = $_.Name $dateigröße = $_.Length $dateibesitze = $_ | Get-Acl | Select-Object -ExpandProperty Owner "DATEI-NAME: $dateiname | DATEI-GRÖSSE: $dateigröße | DATEI-BESITZER: $dateibesitze" } # TODO Aus ForEach-Object besser komplexe Objekte zurück geben: Get-ChildItem -Path 'C:\Temp' -File | ForEach-Object -Process { $datei = New-Object -TypeName PSCustomObject $datei | Add-Member -Name Dateiname -Value $_.Name -MemberType NoteProperty $datei | Add-Member -Name Dateigröße -Value $_.Length -MemberType NoteProperty $datei | Add-Member -Name Dateibesitze -Value ($_ | Get-Acl | Select-Object -ExpandProperty Owner) -MemberType NoteProperty -PassThru } # TODO doer: Get-ChildItem -Path 'C:\Temp' -File | ForEach-Object -Process { $_ | Add-Member -Name Dateibesitze -Value ($_ | Get-Acl | Select-Object -ExpandProperty Owner) -MemberType NoteProperty -PassThru } | Select-Object -Property Name, Length, Dateibesitze # ! Was ist "besser"? $prozesse = Get-Process foreach ($prozess in $prozesse) { $prozess.Name } # vs. Get-Process | ForEach-Object -Process { $_.Name } #endregion #region Tee-Object Get-Process | Tee-Object -FilePath 'c:\temp\EvtlBeendeteProzesse.txt' | Stop-Process -WhatIf # ! NACHTEIL 1: Eine Umleitung ist nur in Dateien möglich # ! NACHTEIL 2: Z.Bsp. Die Auswirkungen von Stop-Process sind im 'Log' nicht enthalten! # ? Eine andere Lösung könnte sein: Get-Process | Stop-Process -WhatIf -PassThru | Set-Content -Path c:\temp\BeendeteProzesseInklFehler.txt # ... Oder per ForEach-Object #endregion #endregion #region Vorhandene Typen/Objekte erweitern # ! siehe .\AKPT\Private\Wissen\B_Basic\B54_Objekte-erweitern.md #endregion #region Auf Ereignisse reagieren #region Asynchrones Ereignis-Handling $np = Start-Process -FilePath notepad -WindowStyle Minimized -PassThru $np | Get-Member -MemberType Event $job = Register-ObjectEvent -InputObject $np -EventName Exited -Action { 'Der Notepad-Prozess wurde beendet.' | Write-Warning } $np | Stop-Process -Force $job | Get-Job $job | Get-EventSubscriber | Unregister-Event # oder: $job | Remove-Job -Force #endregion #region Synchrones Ereignis-Handling $np = Start-Process -FilePath notepad -WindowStyle Minimized -PassThru Register-ObjectEvent -InputObject $np -EventName Exited -SourceIdentifier np.Exited Wait-Event -SourceIdentifier np.Exited "... Die Verarbeitung nach dem Beenden von Notepad wurde aufgenommen." | Write-Warning Unregister-Event -SourceIdentifier np.Exited #endregion #region Hintegrundjobs überwachen $job = Start-Job -Name gci -ScriptBlock { Get-ChildItem -Path c:\*.log -Recurse -Force -ErrorAction Ignore | Select-Object -Property Name, Length, DirectoryName } $job | Get-Member -MemberType Event Register-ObjectEvent -SourceIdentifier job.StateChanged -InputObject $job -EventName StateChanged -Action { "GCI-Job ist fertig, STATUS = $($job.State)" | Write-Warning # TIPP - oder Send-MailMessage Unregister-Event -SourceIdentifier job.StateChanged $job | Remove-Job } $job | Receive-Job -Keep #endregion #region Ordner überwachen # ? Dateien- / Ordner auf Veränderung überwachen $fsw = New-Object -TypeName System.IO.FileSystemWatcher $fsw.Path = "c:\temp" $fsw.Filter = "*.txt" $fsw | Get-Member -MemberType Event $action = { "Dateiänderung: {0} {1}" -f $eventArgs.FullPath, $eventArgs.ChangeType | Write-Warning } Register-ObjectEvent -InputObject $fsw -EventName Changed -Action $action -SourceIdentifier fsw_Changed Register-ObjectEvent -InputObject $fsw -EventName Created -Action $action -SourceIdentifier fsw_Created Register-ObjectEvent -InputObject $fsw -EventName Deleted -Action $action -SourceIdentifier fsw_Deleted Register-ObjectEvent -InputObject $fsw -EventName Error -Action $action -SourceIdentifier fsw_Error Register-ObjectEvent -InputObject $fsw -EventName Renamed -Action $action -SourceIdentifier fsw_Renamed # ! Aufräumen Get-EventSubscriber Unregister-Event -SourceIdentifier fsw_* -Force $fsw.Dispose() $fsw = $null Remove-Variable -Name fsw -Force #endregion #region CIM (früher WMI) Events $action = { $e = $Event.SourceEventArgs.NewEvent "Neuer Process gestartet, ID: {0} | NAME: {1}" -f $e.ProcessId, $e.ProcessName | Write-Warning } Register-CimIndicationEvent -ClassName "Win32_ProcessStartTrace" -SourceIdentifier "ProcessStarted" -Action $action # ! ADMIN-Rechte Get-EventSubscriber -SourceIdentifier "ProcessStarted" Start-Process -FilePath "calc" Unregister-Event -SourceIdentifier "ProcessStarted" #endregion #region Eigene Events auslösen Register-EngineEvent -SourceIdentifier myEvent -Action { "Möööp!" | Write-Warning } New-Event -SourceIdentifier myEvent Unregister-Event -SourceIdentifier myEvent #endregion #endregion #region PowerShell 7 - Update-List # ! Das neue Cmdlet Update-List aktualisiert Listen-Einträge (Add / Remove) von Listen-Objekt-Eigenschaften: class OsDetails { [System.Collections.ArrayList]$ProcessItems OsDetails() { $this.ProcessItems = New-Object -TypeName "System.Collections.ArrayList" } } $osd = New-Object -TypeName "OsDetails" $osd.ProcessItems.AddRange((Get-Process | Select-Object -First 3)) $osd.ProcessItems $notepad = Start-Process "notepad" -PassThru $osd | Update-List -Property "ProcessItems" -Add $notepad | Out-Null $osd.ProcessItems $osd | Update-List -Property "ProcessItems" -Remove $notepad | Out-Null $osd.ProcessItems #endregion # TODO QUIZ - https://attilakrick.com/schlagwort/powershell-objekte/ <# TODO Übung A Finden Sie alle inkl. versteckte und System-Dateien in C:\ und darunter die größer 100MB sind. # TIPP - Get-ChildItem ; Where-Object #> <# TODO Übung B Beenden Sie alle Notepad- und Taschenrechner-Prozesse. # TIPP - Get-Process ; Where-Object -IN ; Stop-Process -WhatIf #> <# TODO Übung C 1. Öffnen Sie sämtliche .log-Dateien in C:\Windows\Logs\*. 2. Anschließend filtern Sie den Inhalt nach dem Wort "Error". 3. Und jetzt schreiben Sie diese Error-Zeile in eine neue zentral MasterError.log-Datei weg. Folgende Informationen sollen enthalten sein: Quell-Dateiname, Zeilennummer, Fehlertext-Zeile. # TIPP - Select-String ; Where-Object ; Set-Content ; Get-ChildItem #> <# TODO Übung D Wie alt (in Tagen) ist die älteste Datei (CreationTime) im C:\Windows-Ordner? # TIPP - Get-ChildItem ; New-TimeSpan ; Select-Object ; Sort-Object #> |