Private/Wissen/B_Basic/B17_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 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # ! Es ist wichtig im Vorfeld zu analysieren: ! # ! A) Um welche Art (Type) von (Rückgabe-)Objekt es sich handelt (Get-Member (gm)). ! # ! B) Was mit diesem Objekt möglich ist (Properties, Methods, Events) (Get-Member (gm)). ! # ! C) Welche Werte enthalten die Eigenschaften (Select-Object, (select))? ! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #region BEISPIEL 1: Wann ist der Zielhost erreichbar? Test-NetConnection -ComputerName 127.0.0.1 | Get-Member # zu A) siehe: TypeName: TestNetConnectionResult Test-NetConnection -ComputerName 127.0.0.1 | Get-Member # zu B) siehe: Name, MemberType, Definition Test-NetConnection -ComputerName 127.0.0.1 | Select-Object -Property * # zu C) siehe: PropertyName : PropertyValue # ! ANTWORT: Wenn die Property 'PingSucceeded' den Wert 'true' enthält, dann ist der Zeilhost erreichbar: $result = Test-NetConnection -ComputerName 127.0.0.1 | Select-Object -ExpandProperty 'PingSucceeded' if($result -eq $true) { 'Mach was auf dem Zielhost!' } #endregion #region BTW: .EXE-Befehle meiden, da diese NUR String-Objekte zurück liefern und daher nur String-Operationen möglich sind: ping.exe 127.0.0.1 | Get-Member # => String => String-Operationen # ! '.exe'-Befehle liefern nur String-Auflistungen zurück deren Weiterverarbeitung umständlich ist: $result = ping.exe 127.0.0.1 | Select-String -Pattern 'Verloren = 0' | Measure-Object | Select-Object -ExpandProperty Count if($result -gt 0) { 'Mach was auf dem Zielhost!' } #endregion #region BEISPIEL 2: Welche Prozesse sind von der Firma Microsoft? # TODO ANALYSIEREN Welche Objekte 'Get-Process' zurück liefert und welche Properties brauchbare Werte enthalten könnte: Get-Process | Get-Member # ! ZUM BEISPIEL: System.Diagnostics.Process (ProcessName, Company, Description, Product, Site) # TODO ANALYSIEREN Welche tatsächlichen Werte enthalten die vermeintlichen 'brauchbaren' Properties: Get-Process | Select-Object -Property 'ProcessName', 'Company', 'Description', 'Product', 'Site' # TODO Betroffene Property 'Company' mit dem Filterwert 'Microsoft Corporation' anwenden: Get-Process | Where-Object -Property 'Company' -IEQ -Value 'Microsoft Corporation' #endregion # TIPP Da Get-Member und Select-Object temporär oft benutzt wird steigern Sie Ihre Effizienz, wenn Sie deren Aliase (gm, select) benutzten! # ? GET-MEMBER: Von welchem Typ ist ein Objekt? # ! TypeName : Vollständiger Type-Name # ! z.B.: System .IO .FileInfo # ! NAMESPACE.NAMESPACE.TYPENAME d.h. Für weitere Beschreibung nach 'System.Io.FileInfo' googeln # ? GET-MEMBER: Was kann man mit dem Objekt machen (Methode) # ? GET-MEMBER: Welche Information trägt das Objekt (Property) # ? GET-MEMBER: Über welche Ereignisse kann ich informiert werden (Event) # ! Name => Name des Member's # ! MemberType => Art des Members: Property <TypeName> PropertyName {get; set;} # ! Methode <void> => Ohne Rückgabe # ! <Rückgabetyp> MethodName(EingabeArgumentTypen) # ! Definition => Beschreibung #region BEISPIEL 3: Wie können alle .LOG-Dateien in C:\Windows angezeigt werden? #! 1.) Analysieren (Eingrenzen & Bestimmen): Get-ChildItem -Path 'C:\Windows' -File | Get-Member # * => System.IO.FileInfo (Name, Extension, FullName) #! 2.) Analysieren (Wertebereich festlegen): Get-ChildItem -Path 'C:\Windows' -File | Select-Object -Property 'Name', 'Extension', 'FullName' #! 3.) 1. und 2. Anwenden: Get-ChildItem -Path 'C:\Windows' -File | Where-Object -Property 'Extension' -IEQ -Value '.LOG' #endregion #region BEISPIEL 4: Eine Übersicht aller großen Dateien anzeigen: # ! Analyse + Rückschlüsse: Get-ChildItem -Path 'C:\' -Force -Recurse -File | Get-Member Get-ChildItem -Path 'C:\' -Recurse -File -Force | Select-Object -First 1 | Get-Member Get-ChildItem -Path 'C:\' -Recurse -File -Force | Select-Object -First 10 -Property 'FullName', 'Length', 'Attributes' # ! Anwendung: Get-ChildItem -Path 'C:\' -Recurse -File -Force -ErrorAction 'Ignore' | Where-Object -Property 'Length' -GE -Value 50MB #endregion #region BEISPIEL 5: Eine Übersicht aller Dateien die älter sind als 1 Jahr sind: # ! Analyse + Rückschlüsse: Get-ChildItem -Path 'c:\' -Recurse -File -Force -ErrorAction 'Ignore' | Select-Object -First 1 | Get-Member Get-ChildItem -Path 'c:\' -Recurse -File -Force -ErrorAction 'Ignore' | Select-Object -First 1 -Property 'Name', 'CreationTime', 'LastWriteTime', 'LastAccessTime' Get-Date | Get-Member (Get-Date).AddYears(-1) # ! Anwendung: Get-ChildItem -Path 'c:\' -Recurse -File -Force -ErrorAction 'Ignore' | Where-Object -Property 'LastWriteTime' -LE -Value (Get-Date).AddYears(-1) #endregion #endregion #region Objekt-Analyse für Profis #region TYPE: Enumeration # ! PowerShell 7 - Es gibt eine Autovervollständigung (CTRL + SPACE) für das Zuweisen von Aufzählungs-Werten (Enum) zu Variablen: $ErrorActionPreference = 'Stop' # ? ^ An dieser Stelle in der Console CTRL+SPACE drücken. # ! Enumerationen (Enum) bilden eine häufig benutzt Untermenge von Typen da. Ob ein Parameter eben nur diese Enumeration akzeptiert ist auf den ersten Blick nicht ersichtlich, da die Hilfe nur den Typennamen ausgibt. # ? Handelt es sich bei einem Type X um eine Enumeration? $ErrorActionPreference | Get-Member [System.Management.Automation.ActionPreference].IsEnum [System.Management.Automation.ActionPreference].GetEnumNames() # ! Wenn die Antwort JA lautet erfolgte eine Zuweisung wie folgt: $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop # ? Handelt es sich bei einem Type X um eine Enumeration? Get-Service | Get-Member -Name StartType [System.ServiceProcess.ServiceStartMode].IsEnum [System.ServiceProcess.ServiceStartMode].GetEnumNames() # ! Wenn die Antwort JA lautet erfolgte ein Vergleich wie folgt: #region BEISPIEL 6: Alle Dienste deren Starttyp auf Manuell steht anzeigen: # ! Analyse + Auswertung: Get-Service | Get-Member Get-Service | Select-Object -Property 'Name', 'StartType' # ! Anwenden: # Problematisch da eine automatische Konvertierung nach ServiceStartMode statt findet...: Get-Service | Where-Object -Property 'StartType' -IEQ -Value 'Manual' # TIPP - ... besser so: Get-Service | Where-Object -Property 'StartType' -EQ -Value ([System.ServiceProcess.ServiceStartMode]::Manual) #endregion #region Vorhandene Enumerationen finden function Get-Enum { param ([string]$Value, [String]$Name, [switch]$All) $defaultManifestModules = 'CommonLanguageRuntimeLibrary', 'Microsoft.CSharp.dll', 'Microsoft.Management.Infrastructure.dll', 'Microsoft.PowerShell.Commands.Management.dll', 'Microsoft.PowerShell.commands.Utility.dll', 'System.dll', 'System.Configuration.dll', 'System.Configuration.Install.dll', 'System.Core.dll', 'System.Data.dll', 'System.DirectoryServices.dll', 'System.Management.Automation.dll', 'System.Management.dll', 'System.ServiceProcess.dll', 'System.Transactions.dll', 'System.Xml.dll' [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object -FilterScript {$All -or ($defaultManifestModules -contains $_.ManifestModule)} | ForEach-Object -Process { try { $_.GetExportedTypes() } catch { 'Keine ExportedTypes vorhanden' | Write-Verbose } } | Where-Object -FilterScript { $_.IsEnum -and $_.Name -imatch $Name } | ForEach-Object -Process { return [PSCustomObject]@{ Name = $_.FullName Source = $_.Module.ScopeName Values = [System.Enum]::GetNames($_) } } | Where-Object -Property Values -imatch -Value $Value } Get-Enum -Value 'SilentlyContinue' Get-Enum -Name 'ServiceStartMode' Get-Enum -All | Measure-Object #endregion # ! Es gibt eine Autovervollständigung (CTRL + SPACE) für das Zuweisen von Aufzählungs-Werten (Enum) zu Variablen seit PowerShell 7: $ErrorActionPreference = 'Stop' # ? ^ An dieser Stelle in der Console CTRL+SPACE drücken. #endregion #region TYPE: Class # !!! INHERITANCE !!! # ! 1. Eine Klasse kann von EINER anderen Basisklasse ABGELEITET sein und ERBT so dessen Member/Funktionen. # ! 2. Jede Klasse ist früher oder später von der Klasse 'Object' (alias PSObject) abgeleitet worden. # ! 3. Eine abgeleitete Klasse A ist daher mit der Basisklasse B kompatibel, daher kann auch ein Objekt von Typ A an einen Parameter vom Typ B übergeben werden. # ? Welche Typ A von welchem Typ B abgeleitet wurde erfahren Sie so: "Köln!".PSTypeNames (Get-Service)[0].PSTypeNames (Get-Process)[0].PSTypeNames (Get-Process).PSTypeNames # !!! STATIC MEMBER !!! # ! Objekte werden von einer bestimmten Klasse instansziert und so materialisiert. Erst dann können wir mit diesen Objekten in der PowerShell hantieren. Es gibt jedoch statische Member die direkt an der Klasse aufrufen werden können ohne das ich zuerst eine OBjekt erzeugen muss. # ? Welche statische Member besitzt eine Klasse: Get-Process | Get-Member -Static [System.Diagnostics.Process]::Start('notepad') Get-Date | Get-Member -Static [datetime]::Today # !!! OVERLOADING !!! # ! Eine Methode kann mehrfacht überladen sein. Unter dem gleichen Methoden-Namen wird unterschiedliches Methoden-Verhalten implementiert. Wichtig ist jedoch das die Parameter-Signatur eindeutig ist. # ? Überladungs-Definitionen einer Methode anzeigen $Text = "PowerShell" $Text | Get-Member -Name LastIndexOf $Text.LastIndexOf.OverloadDefinitions #endregion #region GET-MEMBER: Hintergrundwissen # ? Unterschied zwischen der Analyse von Pipeline-Objekten und NICHT-Pipeline-Objekten: 1..99 | Get-Member # ! Analysiert die Objekte in der Pipeline # vs. Get-Member -InputObject (1..99) # ! Analysiert DAS Objekte (1..99) #endregion #region Hilfereiche Erweiterungen zur Analyse # ! Diese Methode wird automatisch beim laden des AKPT-Moduls implementiert $code = { Add-Type -AssemblyName "System.Windows.Forms" $propertyGrid = New-Object -TypeName "System.Windows.Forms.PropertyGrid" $propertyGrid.Dock = [System.Windows.Forms.DockStyle]::Fill $propertyGrid.PropertySort = [System.Windows.Forms.PropertySort]::Alphabetical $propertyGrid.CanShowVisualStyleGlyphs = $true $propertyGrid.CommandsVisibleIfAvailable = $true $propertyGrid.HelpVisible = $true $propertyGrid.ToolbarVisible = $true $propertyGrid.SelectedObject = $this $window = New-Object -TypeName "System.Windows.Forms.Form" $window.Text = $this.ToString() $window.Size = New-Object -TypeName "System.Drawing.Size" -ArgumentList @(600, 800) $window.TopMost = $true $window.Controls.Add($propertyGrid) $window.ShowDialog() | Out-Null } Update-TypeData -MemberType "ScriptMethod" -MemberName "ShowObject" -Value $code -TypeName "System.Object" -Force # ! Beispiel-Aufruf: $p1 = Get-Process | Select-Object -First 1 $p1.ShowObject() # ! Diese Methode wird automatisch beim laden des AKPT-Moduls implementiert $code = { $url = 'https://docs.microsoft.com/de-de/dotnet/api/{0}' -f $this.GetType().FullName Start-Process $url } Update-TypeData -MemberType "ScriptMethod" -MemberName "GetHelp" -Value $code -TypeName "System.Object" -Force # ! Beispiel-Aufruf: $p1 = Get-Process | Select-Object -First 1 $p1.GetHelp() #endregion #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 # d.h. ALLE anderen Eigenschaften werden entfernt Get-Process | Select-Object -Property Name -Unique Get-Process | Select-Object -ExpandProperty Name ; <# vs.#> ; Get-Process | Select-Object -Property Name Get-Process | Select-Object -Skip 3 -First 2 # 4. und 5. Prozess 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! (Get-Process)[5] #endregion #region Where-Object # Einfache Schreibweise 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 Objekt dar! #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 -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' $o2.Visible = $true # VBA for Application #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 Get-ChildItem -Path "C:\Temp" -File | Get-Acl | Get-Member Get-ChildItem -Path "C:\Temp" -File | ForEach-Object -Process { $_ | Select-Object -Property 'Name', 'Length', @{Label = 'Owner' ; Expression = { $_ | Get-Acl | Select-Object -ExpandProperty 'Owner' }} } | Get-Member #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 # ! Die objektorientierte Programmierung (OOP) ist ein, nicht wegzudenkender Bestandteil der PowerShell. Wenn wir mit Objekten in der Shell arbeiten, nutzen wir deren Member um Werte auszulesen oder Aktionen auszulösen. Was ist, wenn die Eigenschaft oder Methode nicht vorhanden ist? # ? Welche Member besitzt ein Process-Objekt Get-Process | Get-Member -View 'Adapted' # ! Wenn nicht, kein Problem! Das Typen-System der PowerShell ist flexibel. Wir erweitern den Typ selbst oder das aktuelle Objekt. Dieses Verfahren hat den Vorteil, dass wir nicht komplett neue Typen erzeugen müssen. Um in diesen anschließend alles bündeln was wir für die nächsten Schritte benötigen. # ? Welche erweiterten Member besitzt ein Process-Objekt Get-Process | Get-Member -View 'Extended' # ? Gruppiert nach Member-Type inkl. Anzahl Get-Process | Get-Member -View "All" | Group-Object -Property "MemberType" -NoElement | Sort-Object -Property "Count" -Descending <# Die Typen-Erweiterung kann auf unterschiedliche Arten erfolgen. In einem Anwendungsfall ist es nötig das Ergebnis unmittelbar bei Objekt-Erstellung zu berechnen und in eine Eigenschaft speichern. In anderen Fällen soll die auftretende Latenz der Berechnung beim unmittelbaren Zugriff auf die Eigenschaft statt finden. * AliasProperty => Alias für eine andere Property * NoteProperty => Einfache R/W Property * ScriptMethode => ScriptBlock-Code der beim Aufruf ausgeführt wird inkl. Parameter/Argumenten-Übergabe * ScriptProperty => ScriptBlock-Code der beim Aufruf ausgeführt wird * PropertySet => Eine Gruppe von Properties #> # ? Für eine Typenerweiterung benötigen wir ein Objekt, z.B. der erste Process: $p1 = Get-Process | Select-Object -First 1 # ? Dem Objekt eine NoteProperty hinzufügen: $p1 | Add-Member -Name 'Jetzt_A' -Value ( Get-Date ) -MemberType 'NoteProperty' # ? Dem Objekt eine ScriptProperty hinzufügen: $p1 | Add-Member -Name 'Jetzt_B' -Value { Get-Date } -MemberType 'ScriptProperty' # ? Dem Objekt eine ScriptMethod hinzufügen: $p1 | Add-Member -Name 'Jetzt_C' -Value { param([int]$Year) return Get-Date -Year $Year } -MemberType 'ScriptMethod' # ? Neue Member anzeigen: $p1 | Get-Member -Name 'Jetzt*' -View 'Extended' # * Beispiel testen: $p1.Jetzt_A # ? NoteProperty aufrufen => statisches Datum $p1.Jetzt_B # ? ScriptProperty aufrufen => dynamisches Datum $p1.Jetzt_C(2050) # ? ScriptMethod aufrufen => dynamisches Datum <# Bis hierhin erweiterte ich Objekte direkt in der Pipeline oder in Variablen (Add-Member). Diese Art von Erweiterung wirkt sich nicht auf andere Objekte des gleichen Typs aus. Auf der einen Seite kann dieses Verhalten ein Vorteil darstellen. Obwohl vom gleichen Typ grenzen sich diese Objekte gegenseitig ab. Wenn n-Objekte vom gleichen Typ gleiche Erweiterung erhalten, ist es nach diesem Verfahren (Add-Member) aufwendig. In diesen Fällen nehmen wir die Erweiterung direkt am Typ vor (Update-TypeData). Die Erweiterung wirkt sich in der Session auf alle Objekte vom Type X aus. #> # ? Session-weite Erweiterungen Update-TypeData -TypeName "System.Diagnostics.Process" -MemberName "WorkingSet64MB" -Value { [int]($this.WorkingSet64 / 1MB) } -MemberType "ScriptProperty" -Force # ? Alle Process-Objekt haben eine neue Property WorkingSet64MB Get-Process | Get-Member -View "Extended" Get-Process | Select-Object -Property 'Name', 'WorkingSet64MB' #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 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://forms.office.com/Pages/ResponsePage.aspx?id=DQSIkWdsW0yxEjajBLZtrQAAAAAAAAAAAAa__Yp1xwFUME9MN1E1TTBTWEdNOE1QNloyS0paVE05Mi4u <# TODO Übungen # Übung A Finden Sie alle inkl. versteckte und System-Dateien in C:\ und darunter die größer 100MB sind. # TIPP - Get-ChildItem ; Where-Object # Übung B Beenden Sie alle Notepad- und Taschenrechner-Prozesse. # TIPP - Get-Process ; Where-Object -IN ; Stop-Process -WhatIf # Ü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 - Where-Object ; Select-String ; Set-Content # Übung D Wie alt (in Tagen) ist die älteste Datei (CreationTime) im C:\Windows-Order? # TIPP - Get-ChildItem ; Sort-Object ; New-TimeSpan ; Select-Object # Übung E Finden Sie die ersten 10 Dateien die größer sind als 80 Megabyte und geben zu jeder Datei folgende Informationen aus: DateiName, DateiBesitzer, DateiGrößeInMB, DateiAlterInTagen # TIPP - Get-ChildItem ; Where-Object ; Select-Object ; Get-Acl ; Get-Date ; Add-Member ; ForEach-Object # Übung F Warum wird die Eigenschaft PSTypeNames von Get-Member nicht angezeigt? "Köln!".PSTypeNames #> |