PowerShell 1-2-3

Vorwort

Liebe(r) Leser(in),

herzlichen Dank an Ihrem Interesse für dieses PowerShell-Buch. Die erste Frage die Sie sich stellen ist wohl ob Sie weiterlesen sollen. Daher möchte ich kurz und knapp beschreiben an wen sich dieses Buch richtet und ob es für Sie geeignet ist. PowerShell ist eine Skriptsprache die auf allen, aktuellen Microsoft Betriebssystemen zur Verfügung steht. Sicher ist Ihnen schon einmal eine sogenannte DOS-Box oder auch Eingabeaufforderung genannt in Windows begegnet. Diese DOS-Box ist ein Relikt aus den Urzeiten als Microsoft mit dem ersten Betriebssystem DOS an den Start ging. Die PowerShell hat sich nun langsam als Ablösung etabliert und wird über kurz oder lang die gute, alte DOS-Box komplett ablösen. Als normaler Anwender hatten Sie damit bislang wohl weniger zu tun, aber als IT-Profi kommt man nicht drum herum den einen oder anderen Befehl auch einmal auf der Konsole (in die DOS-Box) einzutippen. Administratoren hingegen automatisieren viele tägliche Aufgaben mit Hilfe von Skripten. Dieses Buch erhebt den Anspruch sowohl Administratoren mit langjähriger Skripterfahrung für die Möglichkeiten mit der PowerShell zu begeistern, als auch interessierte Anwender, durch die für Jedermann verständlichen Formulierungen in die Materie einzuführen. Facebook-Maus-Schubser sollten jetzt bitte zuklappen. Ja, jetzt - fertig!

Die 2. Frage dürfte wohl sein, wer das eigentlich ist, der dieses Buch geschrieben hat. Nun, mein Name ist Martin Lehmann und ich hatte mit 14 Jahren das erste Mal einen Computer unter dem Weihnachtsbaum liegen. Die Winterferien waren somit verplant…RTFM for Basic-Programming, oder zu Deutsch: erst einmal Basic programmieren lernen, denn Spiele waren nicht mit dabei. Da Basic zu langsam war, habe ich ein Jahr später ein Assembler-Modul und sogar eine „Datasette“ bekommen. Den ersten PC in Form eines 386er habe ich mir dann selbst zusammen gebastelt um festzustellen, dass die Programmierung auf dem PC als Hobby nicht durchführbar ist. Basic war damals viel zu langsam und allein um meine Grafikkarte in Assembler zu programmieren habe ich ½ Jahr gebraucht. Inzwischen sind die Computer aber so rasend schnell, dass man sogar mit einer Skriptsprache wie PowerShell schon ganze Anwendungen im Handumdrehen zusammen programmieren kann und diese sogar recht flott ablaufen. Da man hier in Deutschland ohne Zeugnis oder Dipl. nicht besonders weit kommt, habe ich ein Fernstudium zum Dipl. PC-Betreuer bei der SGD belegt. Da stellte man fest, dass ich wohl in der Lage sein könnte selbst für die SGD Seminare durchzuführen. So bin ich seit 1995 als freiberuflicher IT-Trainer unterwegs. Begonnen hat es mit Hardware- und Microsoft-Office-Schulungen und später dann Ausbildungen zu Netzwerkadministratoren und dem inzwischen verfügbaren Ausbildungsberuf Mechatroniker. 1997 habe ich den Segen von Microsoft erhalten, das Evangelium zu verkünden, sprich ich wurde MCT. 1998 wurde ich aufgrund meines Erfolgs vom Deutschen Fernschulverband zum Fernschüler des Jahres gewählt. Unter Windows 2000 war ich dann einer der „Early-Achiever“, musste allerdings feststellen, dass Windows 2000 viele Mängel aufwies und so habe ich erst einmal Microsoft den Rücken gekehrt und habe mich den LINUX-Betriebssystemen zugewandt. So habe ich dann bei Novell den Status CNI und distributionsunabhängig den Status LPIC Level 2 erreicht. So kam ich dann auch zur Skriptsprache Perl. Als XP und Server 2003 veröffentlicht wurden, stellte ich fest, dass Microsoft einen großen Teil seiner Hausaufgaben gemacht hat und Windows wieder praktikabel einsetzbar wurde. PowerShell in Version 1.0 hat mich nicht gerade begeistert, aber seit Version 2.0 würde ich sogar Perl in der Ecke stehen lassen, wenn PowerShell auch plattformunabhängig (der Anfang ist gemacht, mehr dazu im Buch) zur Verfügung stehen würde. In reinen Windows Umgebungen gibt es einfach nichts Besseres als die PowerShell, um schnell, einfach und effizient Alltagsprobleme am PC zu lösen.


 

Danksagung

Zunächst einmal möchte ich mich noch recht herzlich bei meinen vielen Schulungsteilnehmern für die interessanten Fragen zum Thema PowerShell bedanken, deren Lösungen teilweise auch in dieses Buch eingeflossen sind.

Darüber hinaus einen besonderen Dank an Gunnar Brandes, der während der frühen Entwicklung des Buches auf erste Fehler hingewiesen hat.

Da ich mich dazu entschlossen habe, dieses Buch als OpenBook zu veröffentlichen hier die Liste mit den freundlichen, finanziellen Unterstützern, die es mir möglich gemacht haben dieses Werk immer weiter voran zu bringen.

Spender

Fam. Schlauch, Rolf Egner, Elke Trautmann, Christian Weber, Jochen Huehne, Claus Schwab, Sebastian Rubretus, Stefan Kurajew, Sebastian Loof, FrankHietzig, Corvin Jonigk, Uwe Karhof, Tobias Duske, Guido Fedeler, Jens Brinkschmidt, Markus Buecker, Christian Erwin, Andreas Huther, Sascha Zimmer, Sven Scholz, Eike Schwoeppe, Martin Klar, Tobias Berger, Bärbel Koslowski, Mike Schaarschmid, Sven Eybe, Dr. Doris Schnabel, Peter Kriegel, Alex Leistner, Peter Rüttinger, Fam. Fertig, Dominik Gülland, Helmut Knies, Lutz Draeger, Wolfgang Gandre, Thomas Peter, Wilfried Dessin, Helge Rohrbein, Thorsten Oeltjen, Mark Vorländer, Volker Baumann, Jens-Peter Lock, Joseph Collé, Angelo Castro, Sebastian Franz,  Alexander Brückner, Thorsten Schmidt, Jörg  Meuthen, Oliver Braun, Frank Mühlenhart, Philipp Schach, Wai Pang Mak, Uwe Bauer, Andreas Marquardt, Christoph Langer, Markus Kern, Sven Bergau, Daniel Plump, Carsten Zuber, Stephan Rumelt, Martin Wecker, Daniel Biermann, Thomas Frank, Alfred Haack, Rainer Friedrich.


 

Inhalt

1       Einführung. 11

1.1         Wie lesen Sie dieses Buch optimal?. 11

1.2         Warum gibt es die PowerShell und weshalb sollten Sie sie einsetzen?. 12

1.3         Auf welchen Betriebssystemen Sie die PowerShell nutzen können. 13

1.3.1          Windows 10 und Server 2016. 13

1.3.2          Windows 8.1 und Server 2012 R2. 13

1.3.3          Windows 8 und Server 2012. 13

1.3.4          Windows 7 und Server 2008 R2. 13

1.3.5          Windows Vista und Server 2008. 14

1.3.6          Windows XP, Server 2003 und Server 2003 R2. 14

1.3.7          Ältere Betriebssysteme. 14

1.3.8          Andere Plattformen. 14

1.4         Kontrollfragen. 15

2       Versionsunabhängig. 16

2.1         Arbeitsumgebung einrichten. 16

2.1.1          PowerShell starten und die Version ermitteln. 18

2.1.2          Einsetzen von Hilfsmitteln für PowerShell 20

2.1.3          Anpassung der Standard Shell 22

2.1.4          Kontrollfragen. 24

2.2         Übersicht der grundlegenden Befehle. 24

2.2.1          Eingaben an der PowerShell Konsole. 24

2.2.2          Kompatibilität. 26

2.2.3          Anatomie des PowerShell Cmdlets. 28

2.2.4          Hilfefunktionen der PowerShell 29

2.2.5          Alias Cmdlets. 33

2.2.6          Navigation. 35

2.2.7          Das EVA-Prinzip und seine Anwendung. 37

2.2.8          Pipelineverarbeitung und Umleitung von Ein- und Ausgaben. 38

2.2.9          Eingabe Befehle und Variablen kennenlernen und verstehen. 41

2.2.10        Kontrollfragen. 46

2.2.11        Die Grundlagen der Verarbeitung. 47

2.2.12        Ausgabe Befehle kennenlernen und verstehen. 52

2.2.13        Quoting Regeln. 59

2.2.14        Verwenden von Rechen- und Zuweisungs-Operatoren. 63

2.2.15        Kontrollfragen. 64

2.3         Objektorientierung. 65

2.3.1          Theoretische Einführung in objektorientierte Programmierung. 65

2.3.2          Objekt-Eigenschaften in der Praxis. 67

2.3.3          Daten Objekt orientiert filtern. 70

2.3.4          Messen wie viel Zeit eine Verarbeitungskette in Anspruch nimmt. 71

2.3.5          Objekte miteinander vergleichen. 71

2.3.6          Vergleichs-Operatoren und reguläre Ausdrücke. 72

2.3.7          Logische Verkettung von Vergleichs-Operatoren mit Logikoperatoren. 76

2.3.8          Objektmethoden in der Praxis. 79

2.3.9          .NET-Objekte. 97

2.3.10        Kontrollfragen. 103

2.3.11        WMI Objekte. 104

2.3.12        Kontrollfragen. 109

2.3.13        COM Objekte. 109

2.3.14        Kontrollfragen. 111

2.4         Mit Skripten arbeiten. 111

2.4.1          Grundlagen zur Skriptausführung. 112

2.4.2          Gutes Skripting. 114

2.4.3          Kommentare. 115

2.4.4          Einfache Parameterübergabe an Skripte. 116

2.4.5          Im Skript den Skriptpfad herausfinden. 116

2.4.6          Kontrollfragen. 117

2.4.7          Schleifenfunktionen. 117

2.4.8          Kontrollfragen. 123

2.4.9          Bedingte Ausführung. 123

2.4.10        Funktionen. 130

2.4.11        Profil Skripte. 145

2.4.12        Skripttuning. 150

2.4.13        Kontrollfragen. 155

2.4.14        Troubleshooting. 155

2.4.15        Kontrollfragen. 161

3       Unterschiede zwischen PowerShell Version 1.0, 2.0, 3.0, 4.0 und 5.0. 161

3.1         PowerShell 1.0. 161

3.1.1          Module in Version 1.0 (Snap-ins). 162

3.2         PowerShell 2.0. 162

3.2.1          ISE. 162

3.2.2          Troubleshooting in Version 2.0. 163

3.2.3          Kontrollfragen. 165

3.2.4          Allgemeine zusätzliche Befehle der Version 2.0. 165

3.2.5          Module in Version 2.0. 167

3.2.6          Kontrollfragen. 172

3.2.7          PowerShell Remote Zugriff. 172

3.2.8          Kontrollfragen. 183

3.2.9          Jobs. 183

3.2.10        Kontrollfragen. 185

3.2.11        Ereignisse. 186

3.2.12        Transaktionen. 187

3.3         PowerShell 3.0. 189

3.3.1          Hilfe. 189

3.3.2          Show-Command. 190

3.3.3          Vereinfachte Schreibweisen. 190

3.3.4          Neue Systemvariable. 191

3.3.5          Workflows. 191

3.3.6          Scheduled Jobs. 195

3.3.7          PSSession Disconnect. 198

3.3.8          New-PSDrive -Persist. 198

3.3.9          Out-Gridview –Passthru. 198

3.3.10        Module in Version 3.0. 199

3.3.11        PowerShell Remoting. 199

3.3.12        WMI vs. CIM... 201

3.3.13        Anti-Malware Scan Interface (AMSI). 202

3.4         PowerShell 4.0. 202

3.4.1          ISE Steuerung. 202

3.4.2          Vereinfachte Schreibweisen, auch in Workflows. 205

3.4.3          Zusätzliche Cmdlets, Parameter. 206

3.4.4          Desired State Configuration. 208

3.5         PowerShell 5.0. 216

3.5.1          Paketmanager/Softwareinstallation. 216

3.5.2          RunSpaces - Multithreaded statt Multiprocessed Jobs. 217

3.5.3          PowerShell Direct. 217

3.5.4          Umgang mit komprimierten Dateien/Verzeichnissen. 218

3.5.5          PowerShell 5.0 Klassen und Enumeratoren. 218

4       Praxisbeispiele. 237

4.1         Allgemeine Tipps rund um PowerShell 238

4.1.1          Rückfragen automatisch mit Ja beantworten lassen. 238

4.2         Windows Verwaltung. 238

4.2.1          Computermanagement. 238

4.2.2          Filesystem.. 239

4.2.3          Registry. 256

4.2.4          Internet Explorer kontrollieren. 259

4.2.5          32 oder 64 Bit?. 261

4.3         Microsoft-Office. 261

4.3.1          Allgemein. 261

4.3.2          Word. 264

4.3.3          Excel 265

4.3.4          Outlook. 267

4.4         Datenbankzugriffe. 270

4.4.1          Datenbankzugriffe auf  Access. 270

4.4.2          Datenbankzugriffe auf  SQL. 274

4.5         GUI – Grafische Oberflächen mit PowerShell 275

4.5.1          Simple Messagebox. 275

4.5.2          Vom einfachen Fenster zu aufwendigen Gestaltungsdetails. 276

4.5.3          Was sind Controls?. 280

4.5.4          Fensterrahmen und Texte. 280

4.5.5          Knöpfe. 283

4.5.6          Tool-Tips (Hilfetexte) 284

4.5.7          Ladebalken. 285

4.5.8          Eingabefelder. 286

4.5.9          Aktives Element eines Fensters festlegen. 287

4.5.10        Listen Auswahl und DropDown Felder. 287

4.5.11        Kalender. 289

4.5.12        Karteikartenreiter. 291

4.5.13        Gruppieren von Radiobuttons und Checkboxen. 292

4.5.14        Dateien öffnen und speichern Dialoge. 295

4.5.15        Explorer ähnliche Ansichten. 296

4.5.16        Durch Events andere Controls im Fenster beeinflussen. 300

4.5.17        Fensterinhalt mittels Timer aktualisieren. 301

4.5.18        Mit XAML erstellte GUIs einbetten. 302

4.5.19        GUIHelper-Modul 303

4.5.20        Auf Tastendruck reagieren. 324

4.5.21        Online Dokumentation zu allen GUI-Elementen. 324

4.6         Internet-Information-Server (IIS). 324

4.6.1          Grundlagen zu IIS. 324

4.6.2          Technische Details. 326

4.6.3          Navigieren im IIS PS-Drive. 329

4.6.4          Websites und ApplicationPools verwalten. 329

4.7         Hyper-V.. 334

4.7.1          Allgemeine Informationen über die Gastsysteme. 334

4.7.2          Snapshot von mehreren VMs gleichzeitig erstellen. 335

4.7.3          Komplettes Backup von VMs erstellen (VHDs, Config, Snapshots). 335

4.8         Lokale clientseitige Benutzerwerwaltung. 335

4.8.1          Wer bin ich, oder WhoAmI 336

4.8.2          Benutzer Konten Steuerung UAC (User Account Control). 336

4.9         Active Directory. 338

4.9.1          Navigieren im AD PS-Drive. 338

4.9.2          AD-Objekt-Verwaltung. 340

4.9.3          AD, Powershell und mmc in einer Oberfläche kombinieren. 348

4.9.4          Eigene AD – Papierkorb Cmdlets. 375

4.9.5          GUI für AD Papierkorb. 378

4.9.6          Gruppenrichtlinien. 388

4.10      Mit PowerShell fremden XML-Dateien arbeiten. 397

4.10.1        XML-Datei laden. 397

4.10.2        XML-Objekt untersuchen. 398

4.10.3        XPath Syntax um auf einzelne Knoten zu verweisen. 398

4.10.4        Neues XML-Element erstellen. 400

4.10.5        Einem XML-Element Attribute und Werte zuweisen. 400

4.10.6        XML-Element in Dokument plazieren. 400

4.10.7        XML-Attribut löschen. 401

4.10.8        XML-Element löschen. 401

4.10.9        XML-Dokument speichern. 401

5       Anhang. 401

5.1         Lösungen. 401

5.1.1          Kontrollfragen. 402

5.1.2          Übungen. 425

5.1.3          Skripte. 428

5.2         Formatierungen Übersicht. 429

5.2.1          -f Format-Operatoren. 429

5.2.2          Datentypen. 430

5.3         Variablen Übersicht. 431

5.3.1          Automatische Variablen. 431

5.3.2          Umgebungs Variablen. 432

5.3.3          Vordefinierte PowerShell Variablen. 432

5.4         Operatoren Übersicht. 432

5.4.1          Vergleichs-Operatoren. 432

5.4.2          Zuweisungs-Operatoren. 432

5.5         Sonderzeichen Übersicht. 432

5.6         Datenbank-Typen. 432

6       Glossar. 433

7       Index. 436

 


 

Typos

 

Normaler Text                                                     Beschreibung/Erklärung.

Simplified Arabic Fixed               Einzutippende Befehle/Skripte.

Fett                                                                          Wichtige Text/Skriptpassagen und Klick-Reihenfolgen.

Unterstrichen                                                      Wichtige Textpassagen.

Kursiv                                                                      Hier sind sinngemäß Ersetzungen vorzunehmen.
                                                                                  Beispiel: Laufwerk:\Pfad wird zu C:\Programme

Fett+Kursiv                                                           Zu drückende Tasten auf der Tastatur, oder anzuklickende       Knöpfe innerhalf von Fenstern.

1                                                                                Bei Skripten sind die Zeilennummern nicht mit abzutippen!     Sind bei Skripten keine Zeilennummern vorhanden, so ist             jede Zeile mit Enter zu bestätigen.


 

1        Einführung

1.1     Wie lesen Sie dieses Buch optimal?

Im ersten Kapitel erfahren Sie wie Sie für sich am effizientesten durch die vielen Seiten dieses Buches kommen.

Das Buch gliedert sich grob in 6 Teile. Den ersten Teil, die „Einführung“ lesen Sie gerade. Hier geht es lediglich um ein paar einleitende Worte, die der gestandene Admin ruhig überspringen kann. Abgesehen vielleicht von dem Unterpunkt über die unterschiedlichen Betriebssystem-Versionen die PowerShell unterstützen. Hier steht im Großen und Ganzen nur ein bisschen Hintergrundwissen, dass für die tägliche Arbeit aber eher unwichtig ist.

Der 2. Teil, „Versionsunabhängig“  führt ganz sacht in die PowerShell Thematik ein und endet mit der Skriptprogrammierung. Ganz nebenbei wird der Ansatz der Objektorientierung vermittelt. In diesem Teil geht es nicht um Anwendungsbeispiele, sondern um das Verstehen der grundsätzlichen Möglichkeiten und Techniken. Alles in diesem Teil können Sie in jeder PowerShell Version gleichermaßen verwenden. Der 2. Teil ist mit seinen unzähligen Übungen auf sehr gut als Schulungsunterlage für einen Kurs zu verwenden.

Im 3. Teil handelt es sich ähnlich wie im zweiten, um grundsätzliche Möglichkeiten und Techniken, kehrt aber die Unterschiede zwischen den einzelnen Versionen hervor. Insbesondere PowerShell- Cracks sollten sich natürlich die Kapitel zur neuen Version 3.0 durchlesen.

Der 4. Teil enthält viele Einsatzbeispiele für den praktischen Gebrauch im Alltag. Den sollten Sie nicht durcharbeiten, sondern sich anhand der Überschriften die für Sie interessanten Skripte herauspicken.

Im 5. Teil, dem Anhang finden Lösungen zu den Kontrollfragen und Skriptübungen, sowie eine Übersicht über die Möglichkeiten des –f Operators. Eine Auflistung unterstützer Datentypen mit Beschreibung, Operatoren und vordefinierte Variablen ist ebenfalls in diesem Abschnitt.

Wenn irgendwo im Text ein Begriff auftaucht den Sie nicht auf Anhieb verstehen bzw. kennen, schauen Sie doch einfach einmal im 6. Teil, dem Glossar nach. Dort werden viele Begriffe und Abkürzungen erläutert.

Im 7. Teil finden Sie einen Index mit Schlagworten.

Eine Befehlszeilenreferenz enthält dieses Buch nicht, da sich die PowerShell sehr gut selbst dokumentiert. Dazu können Sie z.B. für den Einstieg den Befehl Get-Command eintippen, doch dazu später mehr.

1.2     Warum gibt es die PowerShell und weshalb sollten Sie sie einsetzen?

Die PowerShell ist eine mächtige Skriptsprache mit der sich alle Aufgaben an einem PC  auf einfache Weise automatisieren lassen. Theoretisch könnten Sie sogar eine X-beliebige Anwendung damit erstellen. Um Computerspiele zu programmieren gibt es sicherlich bessere Möglichkeiten, da sie in der Ablaufgeschwindigkeit schnellere Programme produzieren. Ein einfaches Programm zur Kundenverwaltung mit Datenbankzugriff, oder gar ein Programm zur Auftragsabwicklung von der Lieferung bis hin zu Abrechnung wäre ohne weiteres machbar.

Die PowerShell selbst ist komplett .NET basiert. Sie erlaubt uns daher den Zugriff auf das komplette .NET-Framework. Wem das nicht reicht, der kann gerne auch auf COM oder WMI zurückgreifen. Was das alles ist, dazu mehr unter dem Punkt Arbeitsumgebung einrichten. Mit diesen 3 Programmierschnittstellen, stehen Ihnen fast alle Möglichkeiten offen, die auch ein C Programmierer hat. Ein fertig kompiliertes C-Programm würde schneller ablaufen, doch mit der PowerShell haben wir es 100 Mal schneller zusammen programmiert. Umständliche Variablendeklarationen und Typkonvertierungen, instanziieren von Objekten aus Objektklassen, komplizierte Vererbung, Polymorphismus und andere böse Wörter brauchen wir hier zunächst einmal nicht. Hier schreiben Sie Ihre Programme fast so einfach, wie wenn Sie sich mit einem anderen Menschen unterhalten. Selbst die gute alte BOS-Box macht uns die Verwaltung des PCs im Vergleich mit der PowerShell ziemlich schwer. Mit Hilfe dieses Buches haben Sie einen sehr schnellen Einstieg in die PowerShell und können schon nach wenigen Kapiteln intuitiv Ihre eigenen Probleme aus der Praxis lösen. Microsoft hat nicht nur versucht die schnelle und einfache Benutzung der Kommandozeile mit den Möglichkeiten des VBS zu vereinen, sondern dieses Ziel sogar noch weit übertroffen.

Seit Windows 7 und Server 2008 R2 ist die PowerShell auf Microsoft Betriebssystemen standardmäßig installiert und aktiviert. In Windows 8 und Server 2012 gibt es zwar aus Gründen der Kompatibilität noch eine DOS-Box (cmd.exe), aber die Frage ist: Wie lange noch? Die zukunftsträchtigste Möglichkeit etwas zu automatisieren ist also die PowerShell. Die komplette Systemcenter Produktfamilie basiert auf PowerShell Skripten und auch in Exchange und SQL ist Sie eine wichtige Grundlage. Auch der IIS und viele andere Standardwindowsfunktionen können damit administriert werden. Mehr dazu im nachfolgenden Kapitel.

1.3     Auf welchen Betriebssystemen Sie die PowerShell nutzen können

In diesem Kapitel erfahren Sie auf welchen Betriebssystemen Sie die PowerShell unter welchen Umständen einsetzen können und welche Voraussetzungen ggf. geschaffen werden müssen.

Grundvoraussetzung für PowerShell ist ein installiertes .NET-Framework in der Version 2.0 (was das genau ist, dazu mehr im Kapitel Arbeitsumgebung einrichten). Bei Windows 7/Server 2008 R2 oder neueren Microsoft-Betriebssystemen ist dies bereits vorhanden. Bei älteren Betriebssystemen können Sie es in der Regel über die automatischen Updates beziehen oder manuell unter diesem Hyper-Link: https://www.microsoft.com/de-de/download/details.aspx?id=6523

1.3.1   Windows 10 und Server 2016

Windows 10 und Server 2016 sind mit PowerShell Version 5.0 ausgestattet. Um PowerShell zu benutzen drücken Sie kurz auf die Windows-Taste (die Taste mit dem Fahnensymbol links unten auf der Tastatur) und tippen Sie einfach den Begriff PowerShell ein und klicken auf das PowerShell Icon. PowerShell Direct erlaubt den direkten Zugriff eines Hyper-V Hosts auf die Shell der Gäste (ohne den Umweg über PS-Remoting). Ab

1.3.2   Windows 8.1 und Server 2012 R2

Windows 8.1 und Server 2012 R2 haben die PowerShell Version 4.0 an Bord. Sie können jedoch das Windows Management Framework 5.0 (mit PowerShell 5.0) herunterladen und installieren. Dies benötigt zuvor die Installation des .NET-Framework 4.5. Auf dem Server können Skripte standardmäßig remote ausgeführt werden.

1.3.3   Windows 8 und Server 2012

Windows 8 und Server 2012 haben die PowerShell Version 3.0 vorinstalliert. Auch hier ist ein Update auf 4.0 oder gar 5.0 möglich. Auf dem Server ist die Skriptausführung lokal standardmäßig erlaubt, auf dem Client nicht.

1.3.4   Windows 7 und Server 2008 R2

Bei Windows 7 und Server 2008 R2 ist Version 2.0 der PowerShell schon direkt bei der Betriebssysteminstallation mit dabei und kann „Out-of-the-Box“ benutzt werden. Ausnahme Server Core Installationen: Hier müssen Sie erst über den Servermanager das .NET-Framework und dann die PowerShell als Feature nachinstallieren. Dazu tippen Sie an der Eingabeaufforderung des Server-Core die folgenden beiden Befehle jeweils mit Enter bestätigt ein:

ocsetup “NetFx2-ServerCore”

ocsetup “MicrosoftWindowsPowerShell”

Um in die PowerShell in der Version 3.0 zu verwenden, laden Sie sich unter http://www.microsoft.com/en-us/download/details.aspx?id=34595 die Datei Windows6.1-KB2506143-x86.msu für 32-Bit Windows Installationen herunter und installieren Sie diese. Für 64-Bit Windows Installationen ist es die Datei Windows6.1-KB2506143-x64.msu. Ab Service Pack 1 für Windows 7 können Sie sogar PowerShell 5.0 installieren. Allerdings bringen die Updates nicht allzuviel, da die zusätzlichen Befehle wie z. B. Set-NetIPAddress leider nur auf den neueren Betriebssystemen verfügbar sind. Warum auch immer sind diese leider nicht auf älteren Systemen vorhanden. Selbst wenn man die neuste PowerShell-Version installiert.

1.3.5   Windows Vista und Server 2008

Vista und Server 2008 haben die PowerShell in der Version 1.0 zwar mit an Bord, diese ist aber nicht aktiviert und müsste erst mittels des Servermanagers bei 2008 oder bei Vista über die Systemsteuerung aktiviert werden. Details dazu verrate ich nicht, denn wenn Sie Version 1.0 aktiviert haben müssen Sie diese erst wieder deaktivieren bevor Sie Version 2.0 benutzen können. Version 1.0 macht sowieso niemand Spaß, von daher laden Sie sich bitte über den nachfolgend bei XP angegeben Link Ihre Version 2.0 für Vista bzw. Server 2008 herunter und installieren Sie die.

Alternativ können Sie auch hier Version 3.0 verwenden. Laden Sie sich unter http://www.microsoft.com/en-us/download/details.aspx?id=34595 die Datei Windows6.0-KB2506146-x86.msu für 32-Bit Windows Installationen herunter und installieren Sie diese. Für 64-Bit Windows Installationen ist es die Datei Windows6.0-KB2506146-x64.msu.

Für die Fans von Windows Server 2008 in der Core Installation, habe ich leider eine schlechte Nachricht. Server 2008 in der Core Installation unterstütz kein .NET und daher funktioniert hier auch keine PowerShell.

1.3.6   Windows XP, Server 2003 und Server 2003 R2

Diese Betriebssysteme haben von Haus aus erst einmal keine PowerShell mit an Bord. Allerdings können Sie sich die Version 2.0 unter http://support.microsoft.com/kb/968929 sowohl für 32, als auch 64 Bit Betriebssysteme herunterladen. Den Download finden Sie so ziemlich in der Mitte der Internetseite unter der Überschrift Windows Management-Framework Core (WinRM 2.0 und Windows PowerShell 2.0).

Version 3.0 wird für XP und 2003 leider nicht mehr angeboten.

1.3.7   Ältere Betriebssysteme

Auf älteren Betriebssystemen wie Windows 2000, NT 4.0 oder gar Windows 3.11 oder DOS steht Ihnen die PowerShell nicht zur Verfügung. Wer so etwas noch einsetzt, oder gar administrieren muss hat mein aufrichtiges Beileid.

1.3.8   Andere Plattformen

Für Linux und Mac gibt es inzwischen von Microsoft selbst PowerShell Core ab Version 6.x als OpenSource-Projekt auf GitHub. Allerdings sollte man sich von der Versionsnummer nicht täuschen lassen. Diese basiert zurzeit auf .NET Core 2.0 und hat daher nicht ganz den Charme eines PowerShell (ohne Core) 2.0, z.B. fehlen in den frühen Versionen noch die WMI Cmdlets – dafür aber z.B. CIM. Ziel ist es PowerShell Core möglichst kompatibel zu PowerShell zu halten. Daher ist auch mit einer zeitnahen Implementierung der WMI Cmdlets zu rechnen (ist ja letztlich nicht wirklich was anderes als CIM). Auch PowerShell 1.0 war nicht so prikelnd hat aber verdammt schnell zugelegt. Ähnlich erwarte ich das auch für PowerShell Core. Möglicher Weise wird es in Zukunft dann nur noch ein PowerShell Core geben und die „normale“ PowerShell wird eingestellt. Auch lassen sich nicht alle Module nutzen, allen voran Active-Directory und Exchange. Auch wenn Module geladen werden können, bedeutet dies nicht, dass diese in vollem Umfang auch unter PowerShell Core genutzt werden können. Hier soll das ebenfalls auf Github zur Verfügung stehende WindowsPowerShellCompatibilityPack weiterhelfen.

Die Installation kann manuell über GitHub erfolgen oder besser von entsprechende Repositories wie hier am Beispiel für Ubuntu 17.04:

# Importieren der public repository GPG keys

curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -

# Registrieren des Microsoft Ubuntu repository (ab hier muss alles als Benutzer root erfolgen)

sudo curl -o /etc/apt/sources.list.d/microsoft.list https://packages.microsoft.com/config/ubuntu/17.04/prod.list

# Updaten

sudo apt-get update

# Installieren der PowerShell

sudo apt-get install -y powershell

# …und starten der PowerShell

Pwsh

# zum Abschluß kann man auch die Installation noch auf den neusten Stand bringen:

sudo apt-get upgrade powershell

Der Aufruf erfolgt auf Linux Systemen mittels pwsh und auf Windows mit pwsh.exe, statt mit powershell.exe und befindet sich standardmäßig in $env:ProgramFiles\PowerShell\.

1.4     Kontrollfragen

Die Kontrollfragen sollen der Festigung Ihres Wissens dienen. Die Antworten finden Sie im ersten Kapitel des Anhangs. Bis jetzt haben Sie noch nicht viel kennengelernt, daher an dieser Stelle nur 2 Fragen:

Auf welchen Betriebssystemen können Sie die PowerShell verwenden?

Was müssen Sie bei Windows Server 2008 R2 in der Core Installation tun um PowerShell verwenden zu können?

2        Versionsunabhängig

Dies ist der wichtigste Abschnitt im Buch, der zunächst mit einer kleinen Einleitung über die Positionierung der PowerShell im Betriebssystem beginnt. Man könnte auch den Begriff Architektur heranziehen, aber ich halte es für übertrieben und nicht zielführend tatsächlich die komplette Architektur darzustellen und hier so weit ins Detail zu gehen. Im weiteren Verlauf kommen Sie dann Stück für Stück tiefer in die Geheimnisse der PowerShell-Welt. Angefangen beim Start der PowerShell Konsole und Eingabe der ersten Befehle über eine Einführung in die Objektorientierte Programmierung bis hin zum Erstellen von Skripten mit der Powershell. Wie der Titel dieses Buchteils schon sagt, können Sie dies hier in jeder Powershell nutzen – egal welche Version Ihnen gerade zur Verfügung steht. Daher werden Sie sich u. U. an der einen oder anderen Stelle fragen warum eine bessere Möglichkeit hier nicht erwähnt wird. Wenn Sie mögen lunzen Sie doch dann einmal im Inhaltsverzeichnis im Abschnitt Versionsunterschiede, ob dort nicht vielleicht gerade genau Ihr Gedanke beschrieben steht.

2.1     Arbeitsumgebung einrichten

In diesem Kapitel werden ein paar Hintergründe zu PowerShell erläutert. Dieses Kapitel ist nur dann für Sie interessant, wenn Sie mit den Begriffen wie .NET, WMI und COM nichts anfangen können.

Wie bereits im Kapitel Auf welchem Betriebssystemen können Sie die PowerShell wie einsetzen erwähnt setzt die PowerShell auf .NET auf.
.NET who the … is .NET? Oder frei übersetzt: Was ist eigentlich .NET? Ein kleiner geschichtlicher Abriss:
Damals, als ich das letzte Mal von meinem Dinosaurier gefallen bin, da war DOS. Das Disk-Operating-System. Multitasking und so komische Sachen gab es noch nicht. In der Regel war der PC schon mit einer einzelnen Aufgabe hoffnungslos überfordert. Wenn man damals ein Spiel programmieren wollte, musste man das am besten in Assembler (Nullen und Einsen durch die Gegend scheuchen) oder C tun. Das DOS bot einem an über dokumentierte Software Interrupts bestimmte Dinge zu tun, wie z. B. die Grafikkarte anzuweisen Pixel in einer bestimmten Farbe zum Glimmen zu bringen. Als Programmierer konnte man also (in echt natürlich viel umständlicher) einen Befehl schreiben wie: Pixel(100,10,16,16,16) und dann hat das fertig übersetzt Programm nach dem Start einen Bildschirmpunkt an der angegebenen Position und Farbe zum Leuchten gebracht. Das war in der Regel aber immer noch zu langsam für Spiele, von daher hat man sich dann nicht mit dem Betriebssystem über den Funktionsaufruf verständigt, sondern man hat direkt mit der Grafikkarten Hardware über die Darstellung des Punktes verhandelt. Den Code für einen einzelnen Bildschirmpunkt würde wohl hier 3-4 Seiten im Buch mit wilden Befehlsabkürzungen wie z.B. LDA, ROL gefolgt von vielen Zahlen und anderen kryptischen Zeichen ausmachen. Im Laufe der Zeit wurden die PCs schneller und mehr und mehr konnte man die Betriebssystemfunktionen einsetzen um sich viel Tipparbeit zu sparen.
Dann kam die Epoche in der wir noch die Hexen geröstet haben mit Windows for Workgroups, welches Multitasking erlaube. Kurze Zeit darauf wurden wir mit der ersten Office-Version beglückt. Nun hatten die Entwickler von Excel und Word die gleiche Aufgabe, wie z.B. den Datei-öffnen-Dialog. Warum sollte sich das Word Team dazu Gedanken machen wie man von Festplatte Dateien kratzt und das Excel Team auch noch einmal? Dies war die Geburtsstunde der DLL-Dateien, den dynamic-link-libraries. Die DLLs waren keine ausführbaren Programme, sie waren auch keine Programmiersprache, sie waren so etwas Ähnliches wie zuvor die Software Interrupts unter DOS, nur einfacher zu bedienen. Nun konnte der Word-Programmierer einen DLL Aufruf in Form von $a=openfile(„*.doc“) machen und der Excel-Programmierer $a=openfile(„*.xls“). Wie man auf das darunterliegende Dateisystem FAT/NTFS oder gar auf die Hardware (Diskette, Festplatte, etc) zugreift, darum musste sich weder der Excel, noch der Word-Programmierer kümmern. Das hat der Programmierer der DLL für die beiden erledigt, ebenso wie die Anzeige des dazugehörigen Bildschirmdialogs.
Dann kam der Trend der objektorientierten Programmierung und aus einfachen DLLs wurde COM, das Component Object Model. Platt gesagt, eine für alle Anwendungsprogrammierer offenstehende Basis von Funktionen um sich die Arbeit zu erleichtern und einmal geschrieben Code wieder zu verwenden.
Wieder einen Schritt weiter ging dann DCOM, das Distributed Component Object Model, welches die Funktionen dann auch über das Netzwerk für andere PCs bereitstellte.
Mit dem heutigen .NET haben wir dank der OpenSource Unterstützung eine weitere Ergänzung um die plattformübergreifenden Austauschmöglichkeiten. Böse Zungen nennen .NET auch einfach das JAVA von Microsoft.

Die PowerShell selbst wurde auf dem Fundament des .NET-Frameworks erstellt, stellt aber auch die komplette Funktionalität des .NET den Programmierern von PowerShell Skripten zur Verfügung. Durch diese enge Bindung an .NET und die vollständige Weitergabe an den PowerShellSkripter (das werden Sie bald sein) haben Sie dank der .NET-Erweiterbarkeit sogar die Möglichkeit nicht nur eigene PowerShell-Befehle zu erstellen, sondern können sogar das .NET-Framework um Ihre selbst gestalteten Eigenschaften und Funktionen zu erweitern. Was es mit den Eigenschaften und Funktionen auf sich hat können Sie im Kapitel über Objektorientierung nachlesen.

Um die PowerShell noch mächtiger zu machen und auch den Zugang zu Informationen zu vereinfachen hat man noch zwei weitere Objekt-Modelle angeflanscht. Über .NET wäre es zwar möglich Zugriff auf die CPU zu bekommen, aber nicht gerade simpel. Durch die zusätzliche Unterstützung des WMI haben wir hier relativ einfachen Zugriff auf viele Systeminformationen und Einstellungen. WMI=Windows Mord Instrument?! Nein! WMI steht für Windows Management Instrumentation und ist die Microsoft Version des schon länger vorhandenen WBEM-Standards. WBEM steht für Web-based Enterprise Management und kann als Nachfolger des SNMP (Simple-Network-Management-Protokolls) angesehen werden. Einen guten Einstieg in WBEM liefert Wikipedia mit einigen weiterführenden Links zu m Thema unter:
http://de.wikipedia.org/wiki/Web_Based_Enterprise_Management
WBEM bietet einen plattformübergreifenden Zugriff auf grundlegende Systeminformationen und –einstellungen. Angefangen bei Informationen zur Hard- und Softwareausstattung über Lastangaben (z.B. Pakete/Sekunde) bis hin zu Funktionen die Routingtabellen ändern, oder das System rebooten. Wie man das benutzt wird im Kapitel WMI-Objekte erklärt und im Praxisteil finden Sie Anwendungsbeispiele.

Der dritte im Bunde ist das gute, alte COM bzw. DCOM Modell. Die meisten Anwendungen die Sie installieren und auch das Windows Betriebssystem selbst bringen solche COMponenten mit. Wenn Sie z.B. Excel installieren, wird eine Excel COMponente in der Registrierdatenbank veröffentlicht. Die COMponente bringt Ihnen einen einfachen Zugriff auf Excel-Funktionen. Obwohl Sie selbst nicht Excel programmiert haben, bekommen Sie mit Hilfe der PowerShell ganz schnell heraus, wie diese COMponente funktioniert und was Sie alles damit anstellen können. So ist es z. B. möglich damit Excel zu starten und auch gleich eine bestimmte Dokumentvorlage bereit zu stellen, oder eine XLS-Datei nach Ihren Vorgaben zu öffnen. Wie man das grundsätzlich macht wird im Kapitel COM Objekte erklärt und einige Anwendungsbeispiele finden Sie im Praxisteil zu MS-Office.

2.1.1   PowerShell starten und die Version ermitteln

Hier lernen Sie „nur“ wie Sie PowerShell starten können und wie Sie auf einem fremden System herausfinden welche PowerShell Version Ihnen dort zur Verfügung steht. Details zu einem parametrisierten Start werden Sie am Anfang eines guten Buches vergeblich suchen. Wenn Sie also schon einmal eine PowerShell gestartet haben und auch wissen wie man die Versionsnummer ermittelt, ab zum nächsten Kapitel…

Die PowerShell zu starten ist mannigfaltig. Natürlich führen auch hier wieder 1000 Wege zum Ziel. Etwas genauer werden wir uns im Kapitel Grundlagen zur Skriptausführung damit beschäftigen. Daher hier nur kurz die gebräuchlichsten Varianten:

Bei Windows 7, Server 2008 R2 und neueren Versionen finden Sie auf dem Bildschirm links, unten und dort rechts neben dem Windows-Knopf der das Startmenü öffnet das PowerShell Symbol, auf das Sie einfach drauf klicken:

Bei älteren Betriebssystemen, bei denen dieses Icon fehlt, finden Sie die PowerShell im Startmenü unter Alle Programme/Zubehör/Windows PowerShell.

Bei Windows 10 / Server 2016 finden Sie es im Startmenü unter „W“ wie „Windows PowerShell“.

Die folgende Variante klappt auf allen Microsoft Betriebssystemen gleichermaßen gut. Tippen Sie die Windows-Taste und dazu ein ‚R‘ (das sogenannte Run-Command). In die Zeile tippen Sie PowerShell und drücken Enter oder klicken auf den OK-Knopf.

Wenn Sie die PowerShell gestartet haben, ja nach Rechner kann das einen kleinen Augenblick länger dauern, als eine „normale Eingabeaufforderung“ zu starten, gibt es wiederum 2 Arten die Version herauszufinden. Entweder durch Eingabe von $host, oder aber get-host, wie hier in der Abbildung zu sehen:

Sowohl die  PowerShell Variable $host, als auch das Cmdlet get-host liefern jeweils in der 2. Zeile Version: 2.0. Besonders interessant ist aber die Tatsache, dass laut der Titelleiste, Powershell.exe angeblich in einem Verzeichnis v1.0 liegt. Halten wir es demokratisch - die Mehrheit siegt ;-). Die Angaben innerhalb der PowerShell sind korrekt. Lassen Sie sich durch den Pfad des Installationsverzeichnisses nicht ins Bockshorn jagen. Was Variablen und Cmdlets nun genau sind, wird in den weiteren Kapiteln genauer erläutert, hier kann Ihnen dies erst einmal egal sein.

Unter Linux tippen Sie einfach pwsh und wenn Sie PowerShell Scripte starten möchten:

(#!/usr/bin/env pwsh) ScriptName.ps1

2.1.2   Einsetzen von Hilfsmitteln für PowerShell

Sicherlich interessiert es Sie, welche Helferlein Ihnen für die PowerShell zur Verfügung stehen. Daher finden Sie bereits an dieser frühen Stelle eine Aufstellung über die wichtigsten, kostenlosen oder bei kostenpflichtigen Programmen im Lieferumfang enthaltenen Tools. Wenn Sie mögen, können Sie auch dieses Kapitel gerne erst einmal überspringen. Allerdings sollten Sie im Hinterkopf behalten, dass in diesem Abschnitt die Zusatzwerkzeuge erwähnt sind. Die nachfolgende Übersicht erhebt allerdings keinesfalls den Anspruch auf Vollständigkeit!

2.1.2.1   Befehlsumfang erweitern

Der Befehlsumfang von PowerShell kann beliebig erweitert werden, nicht zuletzt sogar durch Sie selbst. Wie das genau funktioniert können Sie im 3. Teil des Buches bei den Versionsunterschieden nachlesen. Einige dieser Befehlserweiterungen sind aber nicht Bestandteil von Windows und müssen daher erst heruntergeladen und installiert werden.

Windows eigene Standard Befehlserweiterungen werden detailliert im 3. Teil des Buches behandelt. Diese beinhalten ab Version 2.0 so interessante Dinge wie Trouble Shooting und Diagnose, Active-Directory-Verwaltung, Steuerung von Servermanager und IIS, um an dieser Stelle nur einmal die interessantesten zu erwähnen.

Je nach installiertem Produkt können die vorhandenen Befehle auch erweitert werden. Dazu zählt z. B. Exchange, SQL und die gesamten System Center Produkte von Microsoft, sowie auch von 3. Herstellern z.B. VMware.

PowerShell management Library for Hyper-V, PowerShellPack und PSCX, die PowerShell Community Extensions wurden bis Ende 2017 als OpenSource Projekt unter http://pscx.codeplex.com/ bereitgestellt. Leider wurde diese aufgrund aktuellerer Möglichkeiten der neuen PowerShell-Versionen eingestellt. D.h. wer tatsächlich noch irgendwo mit XP unterwegs ist, hat leider das Nachsehen und muss endlich über eine Aktualisierung nachdenken.

2.1.2.2   Skripting Werkzeuge

Hier sind Werkzeuge aufgelistet die Sie beim Schreiben von PowerShell Skripten unterstützen.

Notepad ist wohl die simpelste Art PowerShell Skripte zu schreiben und nicht explizit dafür gemacht und von daher weniger empfehlenswert. Dafür ist es aber immer und auf jedem Windows System verfügbar.  Zu finden ist dies im Startmenü unter Alle Programme/Zubehör/Editor oder einfach im Suchfeld des Startmenüs notepad eintippen. Wenn Sie einen Doppelklick auf eine Datei mit der Endung .PS1 machen und kein ISE installiert ist wird auch Notepad mit dem Skript geöffnet.

ISE, oder ausgeschrieben das Integrated Scripting Environment ist Bestandteil der PowerShell ab Version 2.0 und ist ein spezieller Skripteditor für PowerShell Skripte. Zeilennummerierung, Syntax Highlighting, die Hervorhebung durch Einfärbung von Befehlen und Parametern, sowie Debug Funktionen zum Aufspüren von Fehlern in Skripten erleichtern das Entwickeln ungemein. Inzwischen ist es sogar relativ einfach möchlich, den Editor durch eigene PowerShell-Skripte beliebig zu erweitern. Auch in diesem Buch werden wir uns auf dieses Standardwerkzeug verlassen.

PowerGUI von Quest ist noch etwas mächtiger als das ISE der PowerShell 2.0, braucht aber dafür beim Starten etwas länger. Als es noch kein ISE gab, war PowerGui der Skripteditor für PowerShell schlechthin. Vor allem auch durch die Befehlserweiterungen welche z. B. für Active-Directory schon in Version 1.0 zur Verfügung standen hat sich PowerGUI einen Namen gemacht. Aber auch in Version 2.0 der PowerShell kann PowerGUI noch mehr bieten als ISE, z.B. das Anzeigen der Variablenbelegung im Skript. Was Variablen sind, dazu wie schon geschrieben später mehr. PowerGUI stand früher mal unter http://powergui.org/ zum Download bereit. Inzwischen ist es aber von der Internetbühne verschwunden.

PowerShell Scriptomatic kann unter http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=24121 heruntergeladen werden. Sparen Sie sich aber die Mühe, der Name des Werkzeugs lässt mehr erwarten, als es liefert. Ähnlich verhält es sich mit Scriptomatic 2.0 (ehem. WMI Scriptomatic), welches nur Perl, VBS, JScript und Python Abfragen für den WMI Namespace erstellt.

Visual Studio ist auch für PowerShell inzwischen der empfohlene Editor insbesondere in auch in Bezug auf PowerShell Core. Allerdings gibt es auch noch Visual Studio Code, welches schneller startet und auch alles mitbringt, was man sich von einem modernen PowerShell Scripting Tool wünscht.

Natürlich gibt es noch unzählige weitere Editoren wie Eclipse oder Visual-Studio von Microsoft, die Sie mit entsprechenden Plugins für PowerShell-skripting erweitern können. Alle hier aufzulisten, wäre ein eigenständiges, wenn auch kleines Buch.

2.1.2.3   Internet

Hier sind einige Internetadressen angegeben, die Ihnen im Alltag bei der Skripterstellung weiterhelfen können.

Auf http://www.codeplex.com/ werden Sie noch viele weitere „Schätzchen“ über die unter Befehlsumfang genannten Erweiterungen hinaus finden. Dazu geben Sie einfach im Suchfeld auf der Website PowerShell ein und schon werden Sie mit Erweiterungen überschüttet: SharePoint, Admin-Tools, Firewall, u.v.a.m.

http://poshcode.org/ bietet viele zusätzliche PowerShell Befehle, Skripte und Tipps und Tricks. Bevor man sich selbst an die Lösung eines Problems macht ist es auf jeden Fall mal einen Blick wert, ob hier vielleicht das Problem schon gelöst wurde und Sie sich einfach der Skripte bedienen und ggf. an Ihre Bedürfnisse anpassen. Die Website ist auf Englisch.

Von Microsoft bekommen wir den Einstieg zu allen möglichen PowerShell Themen an dieser Stelle: http://technet.microsoft.com/de-de/scriptcenter/dd742419. Dort ist auch ein Link zum Microsoft Script Repository für PowerShell: http://technet.microsoft.com/de-de/scriptcenter/dd742419.

Weiterhin gibt es noch die PowerShell Gallery, die ebenfalls von Microsoft betrieben wird. Hier finden Sie inzwischen jede Menge Skripte und Module für den Alltagsgebrauch. Wenn Sie mal was tolles geskriptet haben und es gerne dem Rest der Welt zugänglich machen möchten, können Sie es auch dort veröffentlichen.

Auch auf meiner Seite http://www.martinlehmann.de/wp/category/scripting/powershell/ kommen immer wieder neue Tipps zu PowerShell, aber auch anderen Themen der IT zusammen. Gerne können Sie meine neuesten Artikel als RSS-Feed abonnieren.

2.1.3   Anpassung der Standard Shell

An dieser Stelle finden Sie Informationen darüber wie Sie auf die Schnelle das Aussehen der PowerShell Oberfläche anpassen. Ich bin auch nicht mehr der Jüngste und hätte vielleicht gerne die Schrift etwas größer. Wie man diese Einstellungen zur Systemvorgabe macht, dass es bei jedem Aufruf der PowerShell automatisiert erfolgt, wird im Kapitel Profil Skripte behandelt. Wer mit den Standardeinstellungen aber zufrieden ist oder diese Möglichkeiten bereits, von der „DOS-Box“ her kennt, kann auch gleich zum nächsten Kapitel wechseln.

Wenn Sie die PowerShell gestartet haben finden Sie in der linken, oberen Fensterecke der PowerShell das Systemmenü, wenn Sie das PowerShell Symbol anklicken.

Bei dem Punkt Eigenschaften können Sie das Dialogfeld Eigenschaften von „Windows PowerShell“ durch Anklicken (wenn ich es nicht extra dazu schreibe ist immer die linke Maustaste gemeint) aufrufen.

Nach Karteikartenreitern sortiert finden Sie hier die Einstellmöglichkeiten:

Optionen enthält Einstellmöglichkeiten für die Größe des Textcursors (der Blinkemann an der Position wo Sie Ihren Text eintippen), für den Befehlsspeicher (History) und Berabeitungsoptionen, wie den Einfüge- oder Überschreibmodus, sowie den QuickEdit-Modus. Ist bei QuickEdit-Modus ein Häkchen gesetzt, fügen Sie direkt den Zwischenablage Inhalt an der Textcursorposition ein, wenn Sie die rechte Maustaste in der PowerShell Konsole betätigen. Wenn nicht, dann erscheint bei Rechtsklick erst ein Kontextmenü, indem Sie auswählen können, was Sie tun möchten. Die wenigsten Leser werden wohl in diesem Bereich Anpassungen vornehmen.

Schriftart ist da schon wesentlich interessanter. Hier können Sie die Schriftart und Größe festlegen. Die Größe dürfte hier wohl die interessantere Einstellmöglichkeit sein. Die Schriftart auf eine schicke Handschrift umzustellen, werden Sie sicher ganz schnell wieder bleiben lassen, wenn Sie merken, dass Sie sich mehr auf das Entziffern der Schrift konzentrieren müssen als auf das, was Sie machen möchten.

Layout ermöglicht Ihnen all das, was Sie mit der Maus direkt am Fenster nicht schon viel schneller erledigt hätten. Abgesehen von der Fensterpuffergröße sind die Einstellungen hier recht überflüssig. Mit der Fensterpuffergröße können Sie die interne Größe des Fensters festlegen. Ist Ihre Fenstergröße auf 100 Zeichen Breite eingestellt, aber der Puffer auf 200, so taucht am unteren Bildschirmrand eine Bildlaufleiste auf, da nicht das gesamte interne Fenster dargestellt werden kann. Das kann sehr tückisch sein, wenn Konsolenausgaben rechtsbündig erfolgen. Diese würde man erst sehen, wenn man die horizontale Bildlaufleiste verwendet um den rechten „internen“ Konsoleninhalt zu sehen. So denkt man oft im ersten Moment: „Hä?! Wieso macht der Befehl nichts? Nicht einmal ein Fehler ist zu sehen? Seltsam!“ In solchen Momenten sollte Ihnen schlagartig einfallen, dass vielleicht irgendwie der Fensterpuffer größer ist, als die Fensterbreite! Blick zum unteren Fensterrand. Ist da eine Bildlaufleiste? Wenn ja, haben wir sicher einen größeren Puffer, als das Fenster breit ist. Mit solchen Problemen kann man sich Stundenlang beschäftigen, wenn man da nicht daran denkt.

Farben ermöglicht Ihnen die Schrift- und Hintergrundfarbe der Konsole, sowie von Popups festzulegen. Vorsicht mit der Ostfriesischen Flagge!!! Weißer Adler, auf weißem Hintergrund ;-).

2.1.4   Kontrollfragen

Wie können Sie PowerShell starten?

Wie finden Sie die Version der PowerShell heraus, die auf dem PC installiert ist?

Wie können Sie die Hintergrundfarbe der aktuell geöffneten PowerShell ändern?

Link zu den Lösungen der Arbeitsumgebung

2.2     Übersicht der grundlegenden Befehle

So jetzt geht es aber endlich los! Ihre ersten Befehle an die PowerShell. Bis hier habe ich immer das Wort Befehl verwendet. Zeit für ein bisschen Besserwisserei. Bei PowerShell gibt es keine Befehle! Hier nennt sich ein Befehl Cmdlet (ausgesprochen: Kommandlet). Wenn man es ganz genau nimmt, dann sind Cmdlets eine besondere Form von Befehlen, nämlich die ureigenen PowerShell Anweisungen. Darüber hinaus gibt es dann noch Applikationen, Funktionen, Aliase u.v.a.m., aber wir wollen am Anfang ja nicht gleich übertreiben.

2.2.1   Eingaben an der PowerShell Konsole

Wie mache ich der PowerShell klar, was ich von ihr will?

Zunächst einmal können Sie sich auf der PowerShell wie zu Hause fühlen, sprich Sie können die PowerShell genauso verwenden, wie Sie bisher mit cmd.exe oder bash gearbeitet haben.

Das bedeutet Sie können Befehle wie dir, del, mkdir, ls oder rm direkt auch unter der PowerShell verwenden. Hüten Sie sich aber vor Schaltern wie /s unter DOS oder –r bei Linux. Warum, erfahren Sie im folgenden Kapitel Kompatibilität. Wenn Sie Pfadangaben machen, können Sie die Windows oder Linux Variante benutzen. Beispielsweise können Sie einen Verzeichniswechsel entweder so:
cd \windows
oder so
cd /windows
durchführen. Schon gewusst? Inzwischen geht das sogar in der cmd.exe! Da Sie sich in 99% der Fälle wohl an einem Windowssystem befinden würde ich daher raten, sich auch an der Windows Schreibweise zu orientieren. Weil wir gerade bei Unterschieden zwischen Linux und Windows sind…Groß- und Kleinschreibung dürfen Sie in der Regel ignorieren. Ob Sie also DIR, dir oder Dir tippen macht hier keinen Unterschied.

Probieren Sie die Ihnen bekannten Befehle doch gleich einmal aus.

Es gibt eine Tabulatorvervollständigung wie in den anderen beiden, oben genannten Konsolen auch. Wen Sie den Begriff Tabulatorvervollständigung bereits kennen, dürfen Sie den folgenden Absatz überspringen.

Tabulatorvervollständigung ist unter Linux ein alter Hut, auf Windowssystem kam dieses Feature irgendwann ab XP in die Kommandozeile. Bei PowerShell war dies wie bei der bash von Anfang an dabei. Bei der Tabulatorvervollständigung müssen Sie nur die ersten Buchstaben eines Kommandos, Parameters oder Verzeichnisses tippen bis es nicht mehr mit einem anderen Kommando, Parameter oder Verzeichnis verwechselt werden kann. Wenn Sie dann die Tabulator-Taste (das ist die Taste mit den zwei Pfeilen in entgegengesetzter Richtung, ganz links auf Ihrer Tastatur. Normaler Weise neben dem Buchstaben  Q) drücken, wird das begonnene Wort automatisch vervollständigt. Sie dürfen aber auch schon Tab (Kurzform von Tabulator) drücken, wenn der Begriff noch nicht eindeutig ist. In diesem Fall wird zunächst die im Alphabet zuerst stehende Variante angezeigt, wenn Sie noch einmal Tab drücken die Zweite usw… Probieren Sie das aus indem Sie nur:
get-
tippen und dann drücken Sie so lange die Tab Taste bis das Cmdlet Get-ChildItem angezeigt wird und dann drücken Sie Enter.

Mit den Cursor-Tasten Links, Rechts…kennt jeder. Aber haben Sie mal probiert dabei die Strg-Taste (auch Control-, Steuerungs- oder String-Taste genannt. Diese befindet sich normaler Weise ganz links und rechts unten auf Ihrer Tastatur. Sie ist wie die Taste für Großbuchstaben 2 x vorhanden) festzuhalten? Damit können Sie Wortweise weiter springen, was bei langen Befehlsketten recht hilfreich sein kann.

Cursor-Tasten hoch, runter lässt Sie in der Befehlshistorie blättern. Für die bash verwöhnten: Nein, Strg+R geht nicht und die Historie wird normaler Weise auch nicht in einer Datei gespeichert. Konsole zu, Konsole auf=Historie weg. Am nächsten kommt Strg+R einer Bash (kann in der Historie suchen) noch die Taste F7 (über den Zahlen, so ziemlich in der Mitte), welche die Historie als nummerierte Liste zum Auswählen anzeigt. Wenn Sie die Nummern schon auswendig kennen, können Sie auch F9 tippen und direkt die Nummer angeben (blind). Alt+F7 löscht die Historie. Die Alt-Taste finden Sie links von der Leertaste (das ist wiederum die Große, unten in der Mitte ;-).

Mit der Taste Pos1 springen Sie an den Zeilenanfang und mit Ende zum Zeilenende wie im Word. Diese beiden Tasten finden Sie normaler Weise entweder beim Ziffernblock, oder in einem eigenen Block zwischen dem Ziffern- und dem Buchstaben-Block über den Cursor-Tasten.

Mit Strg+Pos1 löschen Sie alles von der aktuellen Position bis zum Zeilenanfang. 3 x dürfen Sie raten, was Strg+Ende macht.

Wenn Sie etwas in die Zwischenablage bringen möchten, markieren Sie einfach mit der Maus (Linke Maustaste an einer Stelle festhalten und wo anders hin ziehen, dann Maustaste wieder los lassen) den gewünschten Textbereich. Durch einen anschließenden Rechtsklick wird der eben markierte Text in die Zwischenablage befördert. Wenn Sie jetzt noch einmal Rechtsklicken wird der Inhalt der Zwischenablage an der Position des Textcursors (nicht der Maus) eingefügt. Leider kann man nur rechteckige Bereich markieren und nicht wie in Linux Shells üblich zeilenweise. Wenn das Markieren nicht klappt, ist der Quickedit Modus nicht aktiviert, dann müssen Sie erst einen Rechtsklick machen und den Punkt Markieren im Kontextmenü auswählen (siehe Kapitel Anpassung der Standard Shell, falls Sie das übersprungen haben). Die Standard Shortcuts der Zwischenablage funktionieren hier nicht. Also weder Strg+C, E oder V noch wie in der bash Strg+Shift+C, E oder V. Shift+PageUp/Down geht auch nicht…hey, das ist ein Windowssystem…die Maus beißt nicht ;-). Shift ist die Taste um Großbuchstaben zu schreiben. Alternativ wird sie auch Umschalttaste oder einfach Großschreibtaste genannt. Die PageUp/Down Taste ist auf deutschen Tastaturen oft mit dem Wort Bild beschriftet und ein Pfeil nach oben bzw. unten ist darauf abgebildet.

Den Überschreibmodus können Sie jeder Zeit durch Drücken der Einfügen-Taste (Diese befindet sich in der Nähe der POS1- und Ende-Tasten. Manchmal steht nur Einf darauf, aber bitte nicht mit der Entf-Taste verwechseln!) aktivieren oder deaktivieren. Je nachdem werden die Zeichen die sich rechts von Textcursor befinden bei tippen weitergerückt oder überschrieben. Wie man die eine oder andere Variante zur dauerhaften Systemvorgabe macht steht im Kapitel Anpassung der Standard Shell.

2.2.2   Kompatibilität

Hier wird offengelegt, wie kompatibel die PowerShell mit anderen Shells ist.

Zu PowerShell 1.0 Zeiten hat Microsoft ganz gerne behauptet, die PowerShell sei kompatibel mit der herkömmlichen Eingabeaufforderung, ja und sogar klassische UNIX-Befehle lassen sich in der PowerShell ausführen. Bei genauerem Hinsehen fällt aber auf, dass dies nur die halbe Wahrheit ist.

Starten Sie doch einmal die PowerShell Konsole und tippen Sie dort ein paar Befehle ein, die Sie von DOS oder LINUX her kennen, wie z.B.:

dir
ls
ps
mkdir test
rm test

Klappt doch alles wunderbar, oder? Dann tippen Sie doch einmal:

dir *.txt /s.

Die Eingabeaufforderung schluckt den Befehl ohne Murren und verarbeitet ihn auch korrekt. Die PowerShell (auf der Abbildung im Hintergrund) hingegen spuckt eine Fehlermeldung aus. Der Befehl hätte vom aktuellen Verzeichnis C:\Users\Martin\test\ aus, im aktuellen und allen Unterverzeichnissen (wegen des Parameters /s) nach Dateien mit der Erweiterung .txt suchen sollen. Mit der von PowerShell dargestellten Fehlermeldung kommt man auch erst einmal nicht weiter, bis man dann vor lauter Verzweiflung einmal den ersten Begriff aus der Fehlermeldung get-childitem eintippt. Dabei passiert dasselbe, wie bei dir oder ls. Geben Sie nun

alias [dl]*

ein, stellen Sie in der Ausgabe fest, dass sowohl dir vom DOS, als auch ls vom LINUX einfach nur ein Alias (ein Hinweis oder alternativer Name) auf das PowerShell Cmdlet Get-ChildItem sind. Was ein Alias ist und was man damit anstellen kann finden Sie im Kapitel Alias Cmdlets. Da alle 3 Kommandozeilenbefehle naturgemäß vollkommen unterschiedliche Schalter haben, können Sie sich an Ihren 5 Fingern abzählen, dass Kompatibilität nicht einfach über einen Alias realisiert werden kann. Bei dir ist der Schalter /s, bei ls ist es –r und bei dem eigentlichen PowerShell Cmdlet –recurse. Da dir und ls nur Verweise (Aliase) auf das PowerShell Cmdlet sind, wird bei deren Eingabe quasi Get-ChildItem /s ausgeführt und dies führt eben zu Fehlermeldungen, da /s gar nicht als Parameter verstanden wird, weil Parameter bei PowerShell mit dem – Zeichen eingeleitet werden.

Zusammenfassend kann man also festhalten, dass die PowerShell definitiv eine komplett neue Shell ist, die nur sehr eingeschränkt Kompatibilität mit bestehenden Shells aufweist. Wer schon davon geträumt hat seine alten bat, cmd oder sh Skripte in der PowerShell 1:1 einsetzen zu können, muss sich von dem Gedanken leider verabschieden. Jeder einzelne Schalter in einem Skript muss überprüft werden, wie er in PowerShell umgesetzt werden kann. Dies ist offensichtlich so aufwändig, dass es bis heute meines Wissens nach keinen automatisierten Skriptkonverter gibt. Wenn Sie nach der Lektüre dieses Buches einmal Langeweile haben, können Sie ja einen mit PowerShell schreiben ;-).

2.2.3   Anatomie des PowerShell Cmdlets

Das wir nicht kompatibel sind tut erst einmal weh, aber es ist gut besser zu werden. Man muss eben ab und zu auch einmal ein bisschen leiden, um es hinterher besser zu haben. Ein paar Ihrer alten Gewohnheiten dürfen Sie ja auch erst einmal beibehalten.

Nun ist es Zeit für ein kleines didaktisches Wunder. Sie kennen eigentlich schon die PowerShell Cmdlets, Sie wissen es nur noch nicht!

Ein PowerShell Cmdlet sieht syntaktisch immer gleich aus:

Verb-Noun Anpassung Art

Oder als Praxisbeispiel:

Get-ChildItem Path c:\

Das Verb (oder Tätigkeitswort) bestimmt was Sie tun möchten. Möchten Sie etwas starten ist das Verb: Start. Möchten Sie etwas beenden ist das Verb: Stop. Möchten Sie Informationen haben ist das Verb: Get. Möchten Sie etwas einstellen ist das Verb: Set. Möchten Sie etwas Neues erstellen, ist das Verb: New. Raten Sie mal wie Sie z.B. etwas (de-)aktivieren! Ja, richtig! Enable bzw. Disable.

Das Noun (zu Deutsch: Hauptwort) gibt an, mit was Sie das im Verb festgelegte tun möchten. Also z.B. mit einem Dienst = Service, einem Prozess = Process oder der Ereignisanzeige = Eventlog.

Eigentlich brauchen Sie nur zu überlegen, was Sie tun möchten und die englischen Begriffe einsetzen – fertig! So, Sie wissen also nicht was ich meine, nein? Überlegen Sie doch einmal, wie das PowerShell Cmdlets heißen könnte, um den Computer herunterzufahren oder zu rebooten. Noch ein kleiner Tipp: Das Noun (Hauptwort) wird niemals in der Mehrzahl angegeben. Na, sind Sie darauf gekommen? Die Cmdlets lauten Stop-Computer, um den Computer herunterzufahren und Restart-Computer für den Reboot.

Am Anfang habe ich mich sehr über die langen PowerShell Cmdlet Namen geärgert. Aber durch diesen Aufbau ist es eben der Instinkt, der Ihnen sagt wie der Befehl lautet und nicht das Gedächtnis in das Sie zuerst jede Menge kryptische Kürzel reinprügeln muss. Durch die Aliase können Sie sich dann die Tipparbeit verkürzen. Da ls ein Alias auf Get-ChildItem ist und somit gleichzusetzen, können Sie im Laufe der Zeit immer mehr Alias einsetzen (auswendig lernen) um schneller beim tippen zu werden. Wobei Sie dir oder ls bestimmt nicht großartig büffeln müssen;-).

Die Anpassung wird im Fachjargon Parameter, Switch oder Schalter genannt. Mit den Schaltern legen Sie zusätzliche Informationen fest, die Sie dem Cmdlet mitteilen möchten, damit es seine Aufgabe eben angepasst verrichten kann.  Manche Schalter werden einfach nur angegeben, anderen muss die Art oder Form der Anpassung mitgeteilt werden. Wenn Sie nur Get-ChildItem tippen, haben Sie sich schon einmal Gedanken gemacht wie die PowerShell das auffasst? Stellen Sie sich vor Ihre Frau, oder Ihr Mann ruft Ihnen zu „Hol, die Kinder!“. OK, wenn ich zwei Töchter habe die gerade im Kindergarten sind, dann sind wohl die gemeint. Aber wenn Sie selbst gar keine Kinder haben, welche sollen Sie dann holen? Wie Sie sehen, hat’s die PowerShell schon schwer mit uns Knochensäcken. Wenn wir einfach nur Get-ChildItem schreiben, nimmt die PowerShell vor lauter Verzweiflung (bevor’s daheim wieder Ärger gibt) die nächstbesten Kinder mit, die Sie bekommen kann und das sind nun einmal diejenigen welche gerade im aktuellen Verzeichnis (am Straßenrand) stehen.

Hinweis: Nein, das Buch soll nicht zur Kindesentführung ermutigen! Das hier passiert nur im Computer, nur virtuell. Bitte holen Sie die Bits und Bytes nicht ins richtige Leben. Die ganzen Psychopaten die da draußen rumlaufen haben offensichtlich eine innere Stimme, die unpräzise Anweisungen gibt. Zu denen wollen Sie doch nicht gehören?

Also damit die PowerShell nicht wegen Kindesentführung in den Knast geht, sollten Sie als gute(r) Ehe(frau|mann) Ihre Anweisung möglichst präzise formulieren. Dafür haben Sie die Schalter. Also ein Get-ChildItem Path c:\ entspricht in etwa der Anweisung „Hol die Kinder, von Oma ab!“ Die Oma ist hier unser c:\. Nicht das da jetzt was durcheinander gerät, mit der eingangs erwähnten Syntax. Oma’s sind immer artig, oder? Die Oma=c:\=artig ;-)

Damit kommen wir zum ersten heiligen Cmdlet. 5 davon gibt es. Wenn Sie die kennen, haben Sie schon fast gewonnen.

2.2.3.1   Get-Command

Get-Command ist das 1. heilige Cmdlet. Mit dem Get-Command können Sie sich alle Cmdlets anzeigen lassen, die PowerShell kennt. Falls Sie nicht schon alleine drauf gekommen sind wie das entsprechende Cmdlet heißt, schauen Sie doch einfach einmal in diese Liste. Das Get-Command hat natürlich auch einen Sack voll Parameter. Die beiden wichtigsten will ich hier erwähnen und die sollten Sie sich auch unbedingt für die tägliche Arbeit merken.

Mit dem Schalter –Verb können Sie die ausgegebene Liste auf ein bestimmtes Verb einschränken.

Get-Command –Verb Start

zeigt nur noch die Cmdlets an die etwas starten.

Get-Command –Verb *et

zeigt alle Cmdlets die beim Verb mit et aufhören. Sie dürfen also auch gerne * als Platzhalterzeichen einsetzen. Die Liste würde nun alle Cmdlets mit Get, Set und ein Reset mogelt sich auch noch mit rein, anzeigen.

Mit dem Schalter –Noun können Sie dasselbe mit dem Hauptwort tun. Möchten Sie also wissen was man alles mit Diensten anstellen kann, dann tippen Sie einfach einmal:

Get-Command –Noun Service

und schon haben Sie alle Cmdlets zur Verwaltung von Diensten.

Eigentlich kennen Sie nun schon alle PowerShell Cmdlets. Jetzt müssen wir uns nur noch um den Feinschliff kümmern. ;-)

2.2.4   Hilfefunktionen der PowerShell

Get-Help ist das 2. heilige Cmdlet. Wenn Sie mit Get-Command herausgefunden haben welches Cmdlet für Sie interessant sein könnte, möchten Sie sicherlich wissen wie das jeweilige Cmdlet denn nun genau tickt. Dabei hilft uns Get-Help („Hol‘ Hilfe“) weiter. Im Gegensatz zur restlichen Hilfe unter Windows, die von Version zu Version schlechter wird, ist die Hilfe hier in der PowerShell, das Beste was ich je von einem Computer an Erklärung angeboten bekam. Diese Hilfe ist sogar so klasse, dass dieses Buch auf eine Befehlsreferenz komplett verzichten kann. All das bringt die PowerShell schon von Haus aus mit. Warum also ein Buch neben dran legen, wenn Sie schon alles „always at your fingertips“ haben? Leider gilt das nicht mehr für Version 3.0. Heute muss ja alles Cloud und Online sein. So auch die PowerShell Hilfe. Das meiste, hier beschriebene wird also unter Version 3.0 zumindest in der Deutschen Fassung so zunächst einmal nicht funktionieren. Für die Besonderheiten in der Version 3.0 lesen Sie bitte den Abschnitt Hilfe im Kapitel PowerShell 3.0.

Um Hilfe zu einem Cmdlet zu erhalten können Sie hier nicht einfach /? oder –help hinter das jeweilige Cmdlet schreiben, sondern hier schreiben Sie das Cmdlet Get-Help vor das Cmdlet zu dem Sie gerne eine Erklärung hätten. Beispielsweise so:

Get-Help Get-Process

Die Ausgabe gliedert sich für jedes Cmdlet gleichermaßen in 6 Abschnitte:

Name gibt einfach den Namen des Cmdlets wieder, für das Sie eine Erklärung haben möchten. Wenn Sie hinter Get-Help einen Alias (mehr dazu im nächsten Kapitel) angegeben haben, sehen Sie hier in der Beschreibung aber den Namen des Cmdlets auf den der Alias zeigt. Dies ist auch eine Möglichkeit herauszubekommen, welches Cmdlet von einem Alias verwendet wird.

Übersicht beschreibt in einem knappen Satz was dieses Cmdlet für Sie machen kann. Wenn das nicht Ihren Vorstellungen entspricht, schauen Sie doch noch einmal bei den anderen Cmdlets mit Get-Help rein, die Ihnen das Get-Command zu Ihrer Suche angezeigt hat.

Syntax listet auf wie der Befehl korrekt mit zusätzlichen Informationen versorgt wird. Im nächsten Abschnitt kommt gleich eine detaillierte Erklärung dazu.

Beschreibung liefert ähnlich wie Übersicht eine Information was ich mit dem Cmdlet machen kann, nur detaillierter als in der Übersicht.

Verwandte Links listet Cmdlets auf, die mit diesem verwandt sind. Also Cmdlets mit ähnlichen Funktionen. Sollte dieses hier von der Übersicht oder Beschreibung her Ihren Bedürfnissen nicht gerecht werden, lesen Sie doch einmal die Hilfe zu diesem Cmdlets. Eines wird schon passen. Darüber hinaus finden Sie weiterführende Dokumentation teilweise auch noch in Form von Hyper-Links die Sie Ihrem Webbrowser übergeben können für weiterführende Recherchen.

Hinweise ist ein Standardabschnitt der unter jedem Cmdlet so ziemlich den gleichen Inhalt hat und kann daher ignoriert werden. Zumindest, wenn Sie weiter im Buch gelesen haben ;-).

Im Großen und Ganzen ist damit schon fast alles gesagt. Schauen wir uns aber die Syntax noch etwas genauer an.

Im Falle unseres Beispiels mit Get-Process stehen da gleich 3 Zeilen. Fangen wir einmal mit der 1. Zeile an. Zuerst kommt natürlich der Name des Cmdlet, also Get-Process. Darauf folgt [[-Name] <String[]>]. Die eckigen Klammern sollen uns klar machen, dass die enthaltene Angabe optional ist. Sie müssen dies also nicht schreiben, damit der Befehl irgendetwas macht. Geben Sie z.B. einfach nur Get-Process ein bekommen Sie eine Liste mit allen Prozessen. Dann sollte auch klar sein, warum bei diesem Cmdlet alle weiteren Parameter auch in eckigen Klammern stehen. Sie müssen nämlich gar keine Parameter eintippen.

Wenn Sie nun die äußeren eckigen Klammer wegnehmen bleibt noch [-Name] <String[]>. Hier fällt auf, dass –Name noch einmal eckig geklammert ist, String jedoch in <> eingeschlossen ist. Das soll Sie darauf hinweisen, dass auch der Parameter -Name komplett entfallen kann und Sie einfach einen Prozessnamen oder einen Teil davon übergeben können z.B. so:

Get-Process Power*

Auch hier sind offensichtlich wieder Platzhalterzeichen (das *) zulässig. Der Befehl liefert Ihnen dann nur noch Ihren PowerShell Prozess, statt der kompletten Prozessliste. So können Sie also ganz schnell und einfach nach einem bestimmten Prozessnamen suchen. Aber Vorsicht mit den Platzhaltern! Wenn Sie nun auch PowerPoint gestartet hätten, wäre bei diesem Namensfilter auch der PowerPoint Prozess mit in der ausgegebenen Liste.

Wenn wir die 3 Syntax Zeilen miteinander vergleichen stellen wir fest, dass in den unteren beiden Varianten –Id und –InputObject nicht eckig geklammert sind. Das bedeutet, wenn Sie mit Get-Process nicht nach dem Namen des Prozesses gehen möchten, sondern lieber die Prozessnummer angeben wollen, dann müssen Sie den Schalter –Id unbedingt (keine eckigen Klammern!) gefolgt von der Prozessnummer angeben. Tun wir das nicht, dann würde PowerShell in der Spalte Name nach der Nummer suchen, wie man im folgenden Screenshot sieht:

Grundsätzlich wäre die PowerShell in der Lage anhand des übergebenen Datentyps die Zuordnung für die jeweilige Syntax zu treffen. In diesem Fall funktioniert es aber nicht und die Hilfe weißt uns ja auch darauf hin.

Apropos Datentyp… bei den 3 Varianten sehen wir jeweils beim ersten Parameter <String[]>, <Int32[]> und <Process[]>. String soll uns klar machen, dass hier ein Text erwartet wird, Int32 deutet auf eine Zahl hin und Process auf ein Objektyp vom Typ Process. Mehr über Datentypen erfahren Sie im Kapitel Objekt-Typen identifizieren. Von daher soll das an dieser Stelle erst einmal nur ein Hinweis sein, dass es zwischen Zahlen und Texten einen Unterschied gibt. Die eckigen Klammern ohne Inhalt hinter dem Datentyp sollen Ihnen an dieser Stelle sagen, dass Sie mit Komma getrennt auch gerne mehrere Angaben machen können. So würde

Get-Process Power*,expl*

sowohl den PowerShell Prozess, als auch Ihren Windows-Explorer Prozess liefern, der Ihren Desktop bereitstellt.

Dann sind Ihnen sicher auch die Schalter aufgefallen denen keine Angabe zum Datentyp folgt. Hier sollten Sie aber besser einmal

Get-Help Get-ChildItem

ausführen. Hier sehen Sie als datentyplosen Parameter –Recurse. Er sorgt bei Get-ChildItem für das von dir bekannte /s oder von ls das –r. Sprich entweder will ich nur im angegebenen Verzeichnis suchen, oder auch in Unterverzeichnissen. Fehlt der Parameter –Recurse wird nur im angegebenen Verzeichnis gesucht. Wird –Recurse angegeben sucht Get-ChildItem auch in Unterverzeichnissen.

Die Parameter Namen müssen nicht vollständig eingetippt werden, sondern nur so weit bis diese eindeutig sind. Als Beispiel soll uns hier –Recurse von Get-Childitem dienen. Alle diese Schreibvarianten sind zulässig:

Get-Childitem –Recurse
Get-Childitem –recurse
Get-Childitem –Recurs
Get-Childitem –recur
Get-Childitem –Rec
Get-Childitem –re
Get-Childitem –R

ls -r

Gerade die letzte Variante lässt uns nun wieder Linux Kompatibilität vermuten, aber es ist einfach nur ein “dummer Zufall”. Mehr zur Beziehung zwischen ls und Get-ChildItem verdeutlicht das nachfolgende Kapitel über Alias Cmdlets.

Ganz am Ende der Parameterliste finden Sie immer noch einen Eintrag Common Parameters. Diese Common Parameters sind Schalter, die für alle Cmdlets gleichermaßen gelten. Wir werden an verschiedenen Stellen des Buches auf ein paar interessante CommonParameters eingehen. Wenn Sie allerdings jetzt schon einmal sehen möchten was es alles für CommonParameters gibt tippen Sie Get-Help about_CommonParameters. Zugegeben eine etwas seltsame Syntax. Das ist aber Absicht, denn es soll sich von der normalen Cmdlet Erläuterung abheben. Wenn Sie nur Get-Help about eintippen, bekommen Sie eine Auflistung über sämtlichen „Aboutfiles. Die Aboutfiles erklären alles, was nicht mit einem bestimmten Cmdlet zu tun hat, wie beispielsweise die CommonParameters, die ja für alle Cmdlets gleicher Maßen gelten, aber auch wie man Variablen oder Arrays zurechtkommt, wie die PowerShell rechnet, wie man Werte miteinander vergleicht, wie man Skripte erstellt u.v.a.m. Dazu müssen Sie lediglich Get-Help about_ und nach dem Tiefstrich den Begriff den Sie aus der Liste von Get-Help about (ohne Tiefstrich) entnehmen.

Auch das Get-Help Cmdlet hat Parameter. Hier die wichtigsten:

Mit –Example bekommen Sie Beispiele angezeigt inkl. Erklärung was das Beispiel genau macht.
Mit -Detailed bekommen Sie zu den Parametern noch eine prima Erklärung.
Mit –Full bekommen Sie das volle Programm. Also Parameter Erklärung (noch etwas mehr Info als bei –Detailed) und die Beispiele.

Weiterhin ist es möglich mit Platzhalter-Zeichen die komplette Hilfe zu durchsuchen, z.B.:

Get-Help *WSMAN*

Zeigt Ihnen alle Hilfequellen in denen die Bezeichnung WSMAN vorkommt an.

Wenn Sie noch mehr über Get-Help wissen möchten, können Sie auch gerne Get-Help Get-Help ausprobieren.

Auch bei Get-Help gibt es Aliasnamen, wie im folgenden Kapitel beschrieben, allerdings ist hier eine kleine Besonderheit zu beachten. Help ist kein Alias auf Get-Help, sondern eine Funktion! Auf den ersten Blick liefern Help und Get-Help die gleichen Ergebnisse. Das Get-Help Cmdlet läßt den Hilfetext am Bildschirm durchfluschten. Die Help Funktion hingegen macht nach jeder Bildschirmseite eine Pause und wartet auf das Drücken der Leertaste bevor die nächste Bildschirmseite angezeigt wird. Sollten Sie vorzeitig die seitenweise Darstellung verlassen wollen können Sie die Hilfe mit Strg+C oder einfach nur Q abbrechen. Auf die Funktion Help gibt es allerdings einen Alias und zwar man. Daher führen man und Help eine Seitenweise Darstellung durch, während das eigentliche Cmdlet Get-Help den Hilfeinhalt am Stück ausgibt.

2.2.5   Alias Cmdlets

Ein Alias ist ein alternativer, 2. oder 3. Name für Cmdlet. Da Cmdlets wie im vorangegangenen Kapitel dargestellt meist etwas längere Tipparbeit erfordern, haben Sie die Möglichkeit Aliase einzusetzen und damit die Cmdlets abzukürzen. Wenn Ihnen das Wort Alias nichts sagt, können Sie dieses Wort mit Platzhalter, Synonym, Pseudonym, Akronym oder Verknüpfungen auf ein Cmdlet gleichsetzen. Suchen Sie sich den Begriff raus, den Sie am ehesten damit in Verbindung bringen. Natürlich gibt es Bedeutungsunterschiede zwischen den Worten, aber wir wollen hier ja keinen Deutschunterricht machen, sondern PowerShell vermitteln.

2.2.5.1   Alias Definitionen abfragen

Mit Hilfe von

Get-Alias

können Sie sich eine Liste der bereits definierten Aliase anzeigen lassen.

Auch hier gibt es natürlich wieder die Möglichkeit Parameter anzugeben. Mit

Get-Alias –Definition Get-ChildItem

Bekommen Sie alle Aliase angezeigt, die auf Get-Childitem deuten. Es ist also zunächst einmal egal, ob Sie Get-ChildItem, dir, gci oder ls schreiben. Alle listen Ihnen den aktuellen Verzeichnisinhalt auf. Probieren Sie das ruhig einmal aus.

Das ist übrigens auch der Grund warum bei dir *.txt /s, im Kapitel Kompatibilität, in der Fehlermeldung Get-ChildItem angezeigt wurde. Da dir einfach nur auf Get-ChildItem verweist, wurde entsprechend nicht der DOS-Befehl dir ausgeführt, sondern das PowerShell Cmdlet Get-ChildItem.

Get-Alias dir

also ohne den Schalter –Definition macht die Zuordnung genau umgekehrt. Statt zu einem Cmdlet alle Aliase anzuzeigen, wird ausgegeben welches Cmdlet hinter einem Alias steckt. Noch kürzer könnten Sie einfach alias dir schreiben, oder für die Linux Freaks alias ls. Für Sie selbst auf der Kommandozeile ist das ok. Aber in Skripten an denen vielleicht sogar gemeinsam mit Kollegen arbeitet ein absolutes „no-go“. Dort sollten Sie schön brav Get-Alias –Name dir schreiben. Das sorgt für Klarheit und gut lesbaren Code. Mehr zu guten Skripting im Kapitel über Skripte im Abschnitt Gutes Skripting.

2.2.5.2   Alias Definitionen erstellen, ändern

Mit

Set-Alias Alternative Original

oder genauer

Set-Alias –Name Alternative –Value Original

können Sie selbst einen solchen Alias erstellen. Eine witzige, wenn auch wenig praktikable Möglichkeit wäre zu allen Cmdlets eine Bayerische, Hessische oder Schwäbische Version zu erstellen. Das können Sie an dieser Stelle ja gerne einmal ausprobieren. Z. B. Get-ChildItem auf Hessisch:

Set-Alias Zeisch-e-ma Get-ChildItem

oder

Set-Alias Guck-e-ma-do Get-ChildItem

Wer von Linux zu uns stößt hat nun gleich im Sinn „..“ auf „cd ..“ zuzuweisen. Das geht hier aber mit Aliasen nicht! Dafür gibt es Funktionen. Das Problem liegt dabei nicht daran, dass cd selbst ein Alias auf das Cmdlet Set-Location ist, sondern daran, dass ein Parameter in Form von .. übergeben wurde. Sie können mit Aliasen also nur weitere Namen für Cmdlets festlegen, aber keine Parameter damit verknüpfen.

2.2.5.3   Alias Definitionen ex- und importieren

Alias Definitionen gelten nur so lange bis Sie die aktuelle Konsole in der Sie die Aliase definiert haben wieder schließen. Wenn Sie Ihre eigenen Abkürzungen gerne schon zu Beginn Ihrer Arbeit an der Konsole zur Verfügung hätten stehen Ihnen zwei Möglichkeiten offen.

1.       Können Sie die definierten Aliase in eine Datei exportieren. Um die aktuellen Alias Definitionen zu exportieren tippen Sie einfach:
Export-Alias aliasfile.csv
Um diese wieder zu bekommen, sprich zu importieren
Import-Alias aliasfile.csv
Wenn Sie mögen können Sie natürlich vor den Dateinamen auch noch einen Pfad (z. B. Export-Alias c:\powershell\aliasfile.csv) schreiben, falls die Aliasdatei nicht im aktuellen Verzeichnis liegen soll. Als Dateinamenerweiterung habe ich csv gewählt, weil es sich hierbei um eine simple Textdatei handelt, in welche die Zuordnungen einfach durch Komma getrennt hinein geschrieben werden. Das bedeutet, dass Sie die Datei auch mit Notepad oder einem Tabellenkalkulationsprogramm bearbeiten können. Die Endung muss nicht csv lauten. Genauso gut können Sie z.B. auch txt oder auch gar keine Erweiterung angeben.

2.       Können Sie Ihre Alias Definitionen im Profile Skript hinterlegen. Mehr dazu im Kapitel Profil Skripte.

Wer im vorigen Kapitel gut aufgepasst hat, wird sich fragen warum ich hier nicht das Cmdlet New-Alias verwendet habe. Sie haben Recht! Um neue Aliase anzulegen würde man eigentlich das Cmdlet New-Alias verwenden. Mit Set-Alias klappt das allerdings genauso gut. Der kleine aber feine Unterschied liegt darin, dass mit New-Alias wirklich nur noch nicht definierte Aliase angelegt werden können. Existiert schon eine Definition würde New-Alias einen Fehler melden, während Set-Alias die bestehende Zuordnung einfach abändert. Set-Alias ist also flexibler, setzt aber voraus, dass ich weiß was tue. Ein versehentliches Überschreiben ist bei Set-Alias gut möglich, erlaubt mir aber auch bestehende Vorgaben zu überschreiben. Aliase die vom System vorgegeben sind, werfen allerdings auch bei einem einfachen Set-Alias erst einmal einen Fehler. Um selbst Systemaliase wie ls oder dir zu überschreiben, müssten Sie noch den Parameter –option Allscope dranhängen. Das komplette Kommando würde dann so aussehen:

Set-Alias dir get-process –option Allscope

Nach diesem Befehl zeigt der Alias dir nicht mehr auf Get-ChildItem, sondern auf Get-Process! Ob das sinnvoll ist lassen wir einmal dahingestellt, aber um Kollegen zu ärgern und Spaß zu haben taugt es allemal.

2.2.6   Navigation

Vielleicht haben Sie sich ja auch schon einmal gefragt, warum Get-Childitem so seltsam heißt. Warum nennt sich das nicht einfach Get-Directory? Oder der Alias von cd nennt sich auch nicht Change-Directory, sondern Set-Location. Nun, ich glaube das wird am besten deutlich, wenn Sie einmal folgendes eintippen:

Set-Location HKLM:

Get-ChildItem

und nun

cd Software

ls

Ja, genau! Sie stehen gerade mitten im HKEY_Local_Machine der Registry und können hier mit den ganz normalen Befehlen, die Sie vom Dateisystem her kennen navigieren.

Gehen Sie doch einmal in den Hauptschlüssel Current_User, da können Sie auch gleich ein bisschen was anstellen:

cd HKCU:

ls

mkdir Abgefahren

ls

regedit

Jetzt ist der Regedit (ein grafisches Werkzeug zur Bearbeitung der Registrierdatenbank) geöffnet. Schauen Sie doch einmal in den Current_User Schlüssel, ob es da einen Unterschlüssel namens Abgefahren gibt. Schließen Sie Regedit wieder und löschen Sie auch den Schlüssel Abgefahren wieder über die PowerShell.

Windows-like:

del Abgefahren

oder Linux-like:

rm Abgefahren

Prüfen Sie, ob der Key auch wirklich entfernt wurde.

Windows-like:

dir

oder Linux-like:

ls

Ihnen ist das mit der Registry nicht ganz geheuer und Sie möchten gerne lieber wieder zurück zu Laufwerk C:? Kein Problem:

cd c:

Der ganze Spuck basiert auf sogenannten PowerShell Drives, oder kurz PSDrive.Da gibt es natürlich auch gleich ein paar Cmdlets dazu. Um zu erfahren wo Sie überall herumspazieren können fragen Sie dieses Cmdlet:

Get-PSDrive

Das bedeutet Sie können, abgesehen von den Festplattenlaufwerken, z.B. auch in die Aliase oder die Variablen hineinspazieren. Da wir die Aliase schon kennen probieren wir es damit gleich einmal aus:

cd alias:

ls

Vielleicht haben Sie aus dem vorangegangenen Abschnitt noch ein paar Aliase hier herumstehen, die Sie nicht mehr brauchen? Wenn nein, legen Sie sich doch einen „Guck-e-ma-do“ Alias, wie im vorangegangenen Abschnitt beschrieben an. Im vorhergehenden Abschnitt haben Sie vielleicht auch bemerkt, dass es kein Cmdlet wie Remove-Alias zum löschen von Aliasen gibt. Nun, jetzt stehen Sie schon hier „in“ den Aliasen, also probieren Sie doch gleich auch einmal ganz frech:

Windows-like:

del Guck-e-ma-do

oder Linux-like:

rm Guck-e-ma-do

Klappt, oder? Wenn nein, prüfen Sie ob die Namen richtig geschrieben sind und ob Sie zuvor einen Alias mit dem entsprechenden Namen erstellt haben. Vor allem müssen Sie aber versuchen den Aliasnamen löschen und nicht das Cmdlet auf den der Alias zeigt!

Wie Sie sehen müssen die PSDrives nicht wie normale Laufwerke aus einem einzelnen Buchstaben bestehen. Der ein oder andere kennt vielleicht das Subst Kommando vom DOS her. Wenn nein, ist auch nicht weiter schlimm. Mit New-PSDrive können wir „Abkürzungen“ anlegen. Pfadangaben zu Netzlaufwerken können manchmal zu lang werden, aber auch lokal kann man sich Abkürzungen basteln. Sie können sich z.B. für das C:\Windows\System32-Verzeichnis eine Abkürzung namens System32 basteln:

New-PSDrive –name System32 –psprovider filesystem –root c:\windows\system32

Der Schalter –name gibt an, wie Ihr Laufwerk angesprochen werden soll (allerdings ohne den Doppelpunkt!). –PSProvider gibt an, welcher „Anbieter“ den Zugriff bereitstellt. In diesem Falle also das Dateisystem. Mit Get-PSDrive sehen Sie in der Spalte Provider welche weiteren Anbieter Ihnen zur Verfügung stehen. Natürlich dürfen Sie sich auch mit Get-Help informieren, was man mit PSDrives so alles anstellen kann. Zum Schluss haben wir noch den Schalter –root, der den Einsprungpunkt Ihres Laufwerks definiert.

Ab sofort können Sie direkt in das Verzeichnis C:\Windows\System32 hinein wechseln in dem Sie einfach tippen:

cd system32:

Wenn Sie die PowerShell Konsole schließen und neu starten ist das genau wie bei den Alias-Definitionen wieder vergessen. Wenn Sie das gerne dauerhaft haben möchten, müssten Sie die Anweisungen in ein Profile-Skript schreiben, wie Kapitel mit Skripten arbeiten im Abschnitt Profil Skripte beschrieben steht.

2.2.7   Das EVA-Prinzip und seine Anwendung

EVA steht für eines der Grundprinzipien in der elektronischen Datenverarbeitung (EDV) oder wie man moderner Weise sagt Informationstechnologie (IT):
Eingabe
Verarbeitung
Ausgabe

In der Regel wollen wir, dass der Computer für uns etwas erledigt. Deshalb muss er natürlich eine Anweisung erhalten, was zu tun ist. Über Eingaben können Sie dem Benutzer eines Skriptes die Möglichkeit geben mit den Skripten, die wir später erstellen werden, etwas mitzuteilen. Gehen wir einmal von einer einfachen Plus Rechenaufgabe aus, bei der wir zwei Zahlen zusammenzählen möchten. Die Masterfrage ist zunächst natürlich welche beiden Zahlen wir addieren möchten. Anstatt diese Zahlen fest ins Skript einzubauen, können Sie diese Zahlen vom Benutzer entgegennehmen. Sie wollen also, dass der Benutzer eine Eingabe macht, welche beiden Zahlen addiert werden sollen und fordern den Benutzer auf nacheinander 2 Zahlen einzugeben. PowerShell Cmdlets die solche Aktionen für Sie durchführen sind Eingabebefehle. Nachdem Sie die Zahlen vom Benutzer erhalten haben, sollen diese zusammengerechnet werden. Dazu benötigen Sie dann Verarbeitungsbefehle. Zum Schluss wollen Sie natürlich, stolz wie Oskar, Ihr Ergebnis präsentieren. Dafür haben Sie dann die Ausgabebefehle, um dem Benutzer Ihr Ergebnis anzuzeigen. Daher schauen wir uns an dieser Stelle ein paar der wichtigsten Cmdlets dafür näher an.

2.2.8   Pipelineverarbeitung und Umleitung von Ein- und Ausgaben

Pipelineverarbeitung kann jede nicht vollkommen antiquierte Shell. Die Pipelineverarbeitung der PowerShell zeichnet sich aber ganz besonders durch Ihre Objektorientierung aus (dazu später mehr in Kapitel Objektorientierung). An dieser Stelle sollen nur die Grundlagen der Ein- und Ausgabeoperationen verdeutlicht werden. Für erfahrenen Skripter von DOS oder bash denen die Bedeutung dieser Zeichen |, <, >, >>, 2>, 2>>, 2>&1 klar ist, kann der Abschnitt Pipelineverarbeitung mit Ausnahme der Eingabeumleitung übersprungen werden.

2.2.8.1   Eingabeumleitung

Das < Zeichen für die Umleitung der Eingabe ist bei der PowerShell nicht zulässig, aber für Powershell Cmdlets auch nicht nötig. Hier haben wir andere effektivere Mechanismen, die Sie im Laufe des Buches noch kennenlernen werden.

2.2.8.2   Die Umleitung von Standardausgaben zur Standardeingabe

Wenn Sie die PowerShell geöffnet haben ist die Standardeingabe die Tastatur und die Standardausgabe der Bildschirm. Sie tippen etwas über die Tastatur ein und PowerShell antwortet entsprechend auf dem Bildschirm.

Get-Command und Get-Help kennen Sie ja bereits. Lassen Sie die beiden doch zusammen spielen!

Wenn Sie Get-Command –Verb Read (über die Standardeingabe = Tastatur) eingeben, kommt dabei nur ein einziges Cmdlets heraus: Read-Host. PowerShell präsentiert Ihnen den Fund am Bildschirm, der Standardausgabe. Nun würden Sie normaler Weise Get-Help Read-Host (wieder über die Standardeingabe) eintippen um mehr über dieses Cmdlet heraus zu finden. Die Hilfe würde natürlich auch wieder auf die Standardausgabe (den Bildschirm) geleitet werden.  Das können Sie auch abkürzen, in dem Sie die Ausgabe vom Get-Command direkt zur Eingabe von Get-Help leiten. Durch den senkrechten Strich, die sogenannte Pipe (geht mit der AltGr-Taste und der Taste mit den <> Pfeilen drauf in Kombination) wird die Ausgabe des ersten Befehls zur Eingabe des danach folgenden.

Get-Command –verb read | Get-Help

Diese Technik können Sie abgesehen von Ausgabecmdlets, die selbst die Umleitung durchführen überall und beliebig oft hintereinander einsetzen.

2.2.8.3   Ausgaben in Dateien umleiten

Wenn Sie den Text vom Bildschirm lieber in einer Datei hätten, können Sie die Ausgabe vom Standardausgabegerät dem Bildschirm umleiten in eine Datei Ihrer Wahl.

Get-Childitem C:\Windows > ~\Verzeichnisinhalt.txt

Wenn Sie diesen Befehl ausgeführt haben rührt sich auf dem Bildschirm gar nichts. Die Ausgabe des Bildschirms wurde umgeleitet in Ihr Basisverzeichnis und dort in die Datei Verzeichnisinhalt.txt. Oh, Sie wissen nicht wo Ihr Basisverzeichnis ist? Kein Problem! Tippen Sie cd ~ ein und schon stehen Sie in Ihrem Basisverzeichnis. Dort sollten Sie nun durch eintippen des Cmdlet Get-Childitem die Datei Verzeichnisinhalt.txt sehen. Dieses Verzeichnis sollten Sie sich merken. Wie es heißt, sehen Sie am Eingabeprompt, also da wo PS C:…> steht. Öffnen Sie nun den Explorer und navigieren Sie in das Verzeichnis und doppelklicken Sie die Datei Verzeichnisinhalt.txt. Sehen Sie nun, wo Ihre Ausgabe gelandet ist? Schließen Sie die Datei (bzw. Ihr Notepad) wieder.

2.2.8.4   Fehlermeldungen in Dateien umleiten

Wenn Sie nun einmal absichtlich einen Fehler produzieren indem Sie z.B. ein Verzeichnis anzeigen lassen, dass es nicht gibt:

Get-ChildItem C:\XYZ123

dann taucht eine in Rot dargestellt Fehlermeldung auf dem Bildschirm auf. Probieren Sie nun einmal:

Get-ChildItem C:\XYZ123 > ~\Verzeichnisinhalt.txt

Öffnen Sie noch einmal die Datei Verzeichnisinhalt. Und?! Ups…nix mehr drin. Hier wurde nun ein Fehler produziert. D.h. der Befehl ist fehlgeschlagen und hat somit keine erfolgreiche Ausgabe (nichts!) produziert. Also hat er „Nichts“ in die Datei Verzeichnisinhalt.txt umgeleitet und somit deren vorherigen Inhalt gelöscht.

Möchten Sie Fehlermeldungen in Dateien festhalten, müssen Sie den Fehlerausgabekanal bemühen. Der Kanal für Fehlermeldungen hat die Nummer 2. und der Kanal für die Standardausgabe (für erfolgreiche Befehle) ist Nummer 1. Wenn Sie wie bisher keine weiteren Angaben machen ist immer Kanal Nr. 1 (Erfolg) gemeint. Nun schalten wir einmal auf Kanal 2 um (da steht genau dasselbe wie oben, nur in der Mitte habe ich eine 2 hineingemogelt ;-):

Get-ChildItem C:\XYZ123 2> ~\Verzeichnisinhalt.txt

Am Bildschirm ist nun wieder nichts passiert. Wenn Sie nun allerdings wieder die Datei öffnen, ist die Fehlermeldung darin zu lesen. Das Rot von der Schrift in der PowerShell Meldung geht dabei verloren, da Textdateien keine Farben und auch sonst keine Formatierungen (fett, unterstrichen, etc…) unterstützen. Bitte schließen Sie die Datei wieder.

2.2.8.5   Ausgaben und Fehler in dieselbe Datei leiten

Jetzt wollen wir aber auf jeden Fall die Ausgabe der PowerShell in der Datei haben, egal ob es schief gegangen ist, oder nicht. Dazu müssen wir beide Kanäle (Fehler + Erfolg) in die Datei umleiten. Da kommt man so leicht nicht drauf wie das zu schreiben ist:

Get-ChildItem C:\Windows 2>&1 > ~\Verzeichnisinhalt.txt

Sie leiten 2 & (und) 1 in die Standardausgabe (den Bildschirm) um. Und von da aus weiter in die Datei. Wenn Sie die Datei öffnen, sehen Sie den Verzeichnisinhalt (die vorherige Fehlermeldung wurde wieder überschrieben). Schließen Sie bitte wieder die Datei und geben Sie nun

Get-ChildItem C:\XYZ123 2>&1 > ~\Verzeichnisinhalt.txt

ein. Öffnen Sie die Datei und Sie sehen nun wieder die Fehlermeldung, die nun wiederum den Verzeichnisinhalt ersetzt hat. Mit der Angabe 2>&1 > werden also sowohl die erfolgreichen Ausgaben von Befehlen in die Datei geleitet, als auch auftretende Fehlermeldungen.

Nur 2>&1 (wie es in der PowerShell Hilfe steht) hat nur den Effekt, dass Fehler- und Erfolgsmeldung auf dem Bildschirm landen.

2.2.8.6   Meldungen an eine Datei anhängen

Stellen Sie sich vor Sie möchten gerne eine Fehlerprotokolldatei, nennen wir Sie Error.log, benutzen um Fehler zu dokumentieren. Bisher haben die Umleitungen den aktuellen Dateiinhalt aber immer überschrieben. Bei einer Protokolldatei sollen aber natürlich alle Fehler, der Reihe nach, aufgelistet werden.

Das ist ganz einfach, egal ob Fehler oder nicht, wenn Sie an eine Datei noch etwas dranhängen möchten, anstatt den aktuellen Inhalt zu überschreiben, nehmen Sie einfach 2 von den Umleitungspfeilen. Für erfolgreiche Befehle >>, für Fehler 2>> und für beide 2>>1&.

Also machen Sie zunächst einmal einen ersten Fehler für die Error.log:

Get-ChildItem C:\XYZ123 2> ~\Error.log

Damit wurde die Datei erstellt und eine erste Zeile hineingeschrieben. Dann wiederholen Sie noch einmal den Befehl, aber ergänzen noch > Zeichen:

Get-ChildItem C:\XYZ123 2>> ~\Error.log

Wenn Sie die Datei Error.log nun mit notepad öffnen werden Sie 2 Fehlermeldungen sehen.

2.2.8.7   Ausgabe in Dateien und auf dem Bildschirm

Vielleicht möchten Sie auch gerne auf dem Bildschirm sehen was los ist und das auch zusätzlich noch in eine Datei schreiben. Den Linux Leuten fällt dazu nichts Besseres ein als einen Tee zu trinken, den PowerShell Skriptern auch und die DOS-Leute haben nicht einmal den Funken einer Idee ;-).

In der PowerShell haben Sie ein Tee-Object Cmdlet, dass Sie aber gerne beim Alias tee nennen dürfen.

ls c:/ | tee ~\Verzeichnisinhalt.txt

Da fühlen sich die Linuxianer doch gleich wie zu Hause ;-). Die Windows-Leute dürfen es natürlich auch so schreiben:

dir c:\ | tee ~\Verzeichnisinhalt.txt

Und die PowerShell Hardliner (syntaktisch 100% PowerShell):

Get-ChildItem -Path “C:\” | Tee-Object –FilePath “~\Verzeichnisinhalt.txt”

Egal für welche der drei Varianten Sie sich entscheiden, dass Ergebnis ist dasselbe. Die Ausgabe des Inhalts von Laufwerk C:\ erscheint auf dem Bildschirm und wird auch gleich in die Datei Verzeichnisinhalt.txt in Ihrem Basisverzeichnis geschrieben. Wenn Sie mögen können Sie mit notepad wieder in die Datei Verzeichnisinhalt.txt hineinschauen.

2.2.8.8   Mehrere Befehle nacheinander ausführen, ohne dass die Ausgabe umgeleitet wird

Wenn Sie mehrere Cmdlets nacheinander ausführen möchten, ohne dass die Ausgabe des vorangegangenen zur Eingabe des nächsten wird. Mit Hilfe eines Semikolons als Trenner wird jeder Befehl für sich ausgeführt und stellt seine Informationen am Bildschirm dar ohne zwischendurch zum Prompt zurück zu kehren.

cd c:\; dir; cd windows; dir

Wechselt zunächst ins Wurzelverzeichnis von Laufwerk c:; zeigt den Inhalt von Laufwerk c: an; wechselt dann ins Windows-Verzeichnis und zeigt direkt nach dem ersten Verzeichnis, das zweite (Windows) an. Diese Technik ist besonders bei mehreren, Zeit intensiven Befehlen sehr zu empfehlen.

2.2.9   Eingabe Befehle und Variablen kennenlernen und verstehen

Eingabe bedeutet immer, dass wir von irgendwo Informationen abholen. In diesem Kapitel erfahren Sie wie Sie Informationen bekommen. Im ersten Abschnitt befassen wir uns mit der wichtigsten Schnittstelle Mensch/Computer. Danach werden einige bedeutsame Cmdlets näher erklärt und schließlich ein allgemeiner Überblick über alle zur Verfügung stehenden Eingabebefehle gegeben.

2.2.9.1   Benutzereingaben entgegennehmen und in Variablen ablegen

In diesem Abschnitt befassen wir uns mit Cmdlets, die uns ermöglichen vom Benutzer Eingaben entgegen zu nehmen.

Die primitivste aber zugleich auch recht flexible Variante ist das Cmdlet Read-Host. Tippen Sie einfach einmal Read-Host ein und bestätigen Sie mit der Enter-Taste. Daraufhin passiert augenscheinlich erst einmal gar nichts. Wenn Sie nun anfangen einen Text einzutippen und diesen auch wieder mit der Enter-Taste bestätigen, plappert Ihnen die PowerShell einfach nach, wie ein Papagei.

Die Eingabe die Sie mit Read-Host gemacht haben muss natürlich irgendwo hin. Die sogenannte Standardausgabe für die PowerShell ist wie bei anderen Shells auch der Bildschirm. Wenn ein Befehl etwas zu sagen hat tut er dies, wenn er nicht anderweitig dazu angewiesen wird, immer auf dem Bildschirm. Daher also der Papagei Effekt.

Wenn Sie später die Eingaben weiter verarbeiten wollen, müssen Sie diese irgendwo unterbringen wo Sie sie wiederfinden. Daher an dieser Stelle eine kleine Einführung in Variablen:

Eine Variable ist eine Möglichkeit Informationen (ganz gleich welcher Art) irgendwo abzulegen, wo Sie diese wiederfinden können. Variablen können frei wählbare Text basierte Bezeichner sein. Wichtig ist nur, dass ein $-Zeichen vorne dran steht. Der Einfachheit halber nehmen wir hier einfach einmal ein a. Wenn Sie statt einfach nur Read-Host, nun lieber

$a=Read-Host 

eingeben und mit Enter bestätigen, ist zunächst einmal kein Unterschied festzustellen. Wenn Sie nun aber Ihren Text eintippen und diesen auch wieder mit Enter bestätigen fällt auf, dass der Papagei ausgeflogen ist. Zumindest hat Ihnen niemand nachgeplappert. Der Text, den Sie soeben eingetippt haben ist nun in $a (Ihre erste Variable) dank der Zuweisung mit dem =-Zeichen. Ihre nächste Frage ist nun unweigerlich: „Wie komme ich denn wieder an den Inhalt?“. Es ist wieder einmal viel einfacher als Sie glauben. Tippen Sie einfach nur $a ein und bestätigen Sie wieder mit Enter. Simpel, oder? Wollen Sie noch einmal? Nur zu…

Mit Read-Host können Sie auch Nachrichten an den Benutzer herausgeben, was Sie überhaupt von Ihm wollen. Tippen Sie dazu

$a=Read-Host “Wie ist Ihr Name

und bestätigen Sie mit Enter. Nun weiß der Benutzer gleich was Sie von ihm wollen und er wird (hoffentlich) auch seinen Namen eingeben. Wenn Sie nun einfach wieder $a tippen und mit Enter bestätigen, sollte der eingegebene Name wiedergegeben werden. Der vorherige Inhalt von $a wurde also einfach überschrieben. Der Wert aus dem vorangegangenen Beispiel ist damit verloren.

Auch Kennwörter können Sie damit entgegennehmen. Dazu brauchen wird nur den Schalter ‑AsSecureString mit anzugeben.

$a=Read-Host „Her mit dem Kennwort–AsSecureString

Das ist so geheim, dass nicht nur die Eingabe mit Sternchenplatzhalterzeichen versehen wird, sondern auch wenn Sie nun versuchen $a auszulesen kommt nur die Rückmeldung System.Security.SecureString - der Verweis auf ein geschütztes .NET-Objekt.

2.2.9.2   Grundlagen der Variablenverwaltung

Welchen Zweck Variablen erfüllen sollte durch den vorangegangenen Abschnitt klar sein. Sie können also in Variablen Informationen hinterlegen. Die Zuweisung kann denkbar einfach funktionieren:

$a=“Inhalt

Ebenso wie die Abfrage des Inhalts:

$a

Den Inhalt einer Variablen zu ändern funktioniert genauso wie das Zuweisen:

$a=“anderer Inhalt

$a

Eine Auflistung aller im Moment verwendeten PowerShell Variablen finden Sie mithilfe des Cmdlets Get-Variable oder kurz über den Alias darauf:

gv

In der Spalte Name steht die Bezeichnung der Variablen und unter Value der aktuelle Inhalt. Irgendwo in dieser Liste müsste also auch in der Spalte mit der Überschrift Name der Buchstabe a (von $a) auftauchen und unterhalb von Inhalt entsprechend das was Sie zugewiesen haben: anderer Inhalt.

Selbstverständlich können Sie auch den Inhalt einer Variablen löschen, indem Sie einfach angeben:

$a=““

Damit haben Sie den Inhalt der Variablen $a gelöscht, doch nicht die Variable selbst, wie ein erneutes Get-Variable beweist. Nun steht in der Spalte Name immer noch das a, aber in der Spalte Value ist nichts mehr zu sehen. Wenn Sie eine Variable komplett löschen und somit den reservierten Speicherplatz freigeben möchten, wie das ein ordentlicher Programmierer/Skripter tut, dann können Sie das Remove-Variable, oder kurz dem Alias darauf rv, tun:

rv a

Achtung! Hier bitte kein $-Zeichen vor die Variablenbezeichnung schreiben. Wenn Sie nun erneut Get-Variable bzw. gv ausführen, stellen Sie fest, dass a nicht mehr in der Spalte Name auftaucht und somit der Speicherplatz, den die Variable im RAM belegt hat freigegeben wurde. Natürlich müssen Sie nicht immer erst den Inhalt löschen, bevor Sie die Variable entfernen, sondern können Remove-Variable auch direkt auf Variablen, die noch einen Inhalt haben, anwenden.

Weitere Informationen zu Variablentypen erhalten Sie im Kapitel Objektorientierung und dort im Abschnitt Objekt-Typen identifizieren. Ein paar Tricks bei der Zuweisung von Werten zu Variablen finden Sie im Kapitel Quoting im Abschnitt Verwenden von Rechen- und Zuweisungs-Operatoren.

2.2.9.3   Benutzeranmeldeinformationen entgegennehmen

Wenn wir gerade solche Angaben wie Benutzername und Kennwort benötigen um z.B. etwas als ein anderer Benutzer durchzuführen hilft uns ein anderes Cmdlet noch besser weiter. Achtung:

$a=Get-Credential

Cool, oder? Ihr erstes grafisches Eingabefenster und Sie haben nicht einmal großartig etwas programmieren müssen.

Das nachfolgende Beispiel wird im Kapitel Remotezugriff genauer erläutert und soll hier nur die Möglichkeiten klar machen. Abtippen wird an dieser Stelle noch nicht funktionieren.

Viele Cmdlets haben einen Schalter –Credential. Sie könnten z.B. zu einem anderen Computer eine Verbindung aufbauen indem Sie zunächst einmal $a=Get-Credential eingeben und danach Enter-PSSession Computername –Credential $a. Oder Sie kombinieren gleich beide Befehle miteinander:

Enter-PSSession Computername –Credential (Get-Credential)

Die runde Klammer an dieser Stelle bewirkt, dass der Teil in den Klammern zuerst ausgeführt wird und dessen Ergebnis an dieser Stelle eingesetzt wird. Das funktioniert also so ähnlich wie in der Mathematik.

2.2.9.4   Weitere besonders interessante Cmdlets für die Eingabe

Get-Alias und Get-Childitem dürfte bereits aus dem Kapitel über Aliase bekannt sein. Ebenso Get-Command und Get-Help aus den ebenfalls vorangegangenen Abschnitten.

2.2.9.4.1    Informationen über Wiederherstellungspunkte

Get-ComputerRestorePoint listet Ihnen Wiederherstellungspunkte des Systems auf. Wenn Sie die PowerShell Konsole nicht als Administrator ausführen bekommen Sie eine Fehlermeldung. Um die Konsole als Administrator zu starten, können Sie entweder einen Rechtsklick auf das PowerShell Icon machen und „Als Administrator ausführen“ auswählen, oder ab Vista aufwärts können Sie auch den Begriff powershell ins Suchfeld des Startmenüs eingeben und mit Strg+Shift+Enter bestätigen. Wiederherstellungspunkte werden teils automatisiert vor dem Einspielen von Updates erstellt. Mit dem zusätzlichen Schalter –LastStatus sehen Sie ob der letzte Wiederherstellungsversuch erfolgreich war. Damit man die Wiederherstellungspunkte nutzen kann muss der Computerschutz aktiviert sein und der Dienst Volumenschattenkopie laufen. Am einfachsten aktivieren Sie diese Funktion mittels eines Rechtsklick auf Computer im Startmenü. In den daraufhin erscheinenden Systemeigenschaften finden Sie links oben den Punkt Computerschutz. Dort können Sie dann für die einzelnen Laufwerke den Computerschutz einschalten und somit auch den Dienst für die Volumenschattenkopien aktivieren.

2.2.9.4.2    Dateiinhalte auslesen

Mit Get-Content können Sie u. a. den Inhalt von Textdateien, also zum Beispiel auch Log-Dateien, auslesen. Erstellen Sie dazu zunächst einen Ordner namens PowerShell auf Ihrem Laufwerk C:. Nun einfach mit notepad eine kleine Textdatei mit beliebigem Inhalt auf Laufwerk c: in Ihrem Ordner unter dem Namen Textdatei.log abspeichern. Um den Inhalt von der PowerShell auszulesen geben Sie gc C:\PowerShell\Textdatei.log ein. gc ist ein Alias auf Get-Content – das tippt sich schneller! Die Linux-Fans dürfen auch gerne cat benutzen. Dies ist ein weiterer Alias auf Get-Content. So Spielchen wie tac machen wir hier unter Windows aber nicht ;-). Wenn Sie unbedingt wollen, können Sie sich gerne später einen tac Befehl basteln.

2.2.9.4.3    Ländereinstellungen abfragen

Mittels Get-Culture können Sie sich über die Spracheinstellungen auf Ihrem System informieren. Get-UICulture bezieht sich auf die Benutzeroberfläche.

2.2.9.4.4    Datum und Uhrzeit abfragen

Get-Date klärt Sie über Datum und Uhrzeit auf.  Wenn Sie nur das Datum haben möchten können Sie den Schalter –DisplayHint Date eingeben oder um nur die Uhrzeit zu erfahren ‑DisplayHint Time.

2.2.9.4.5    Ereignisanzeige auslesen

Mit Get-EventLog Logname können Sie sich Informationen aus der Ereignisanzeige zusammenstellen lassen. Für Logname geben Sie System, Application oder Security ein um die entsprechenden Berichte anzeigen zu lassen. Mit Get-Eventlog –List können Sie sich anzeigen lassen, wie die Lognamen heißen, die Sie abrufen können. Das Sicherheitslog können Sie sich auch wieder nur mit administrativen Berechtigungen anzeigen lassen. Die anderen beiden können Sie aber auch als normaler Benutzer abfragen. Mittels Get-EventLog System ‑Newest 10 bekommen Sie nur die 10 aktuellsten Einträge aus dem Systemlog.  Mit Get-EventLog System –InstanceID 24 bekommen Sie nur die Ereignisse angezeigt, mit der Ereignis-ID 24 (dazu sollten Sie natürlich ein Ereignis mit der Nummer im Systemlog stehen haben. Schauen Sie ggf. in die Ereignisanzeige und probieren Sie mal eine der ersten Nummern, die dort stehen). Mit Hilfe des Schalters –EntryType können Sie sich nur Fehler des Typs Fehler (=“Error"), Informationen (= "Information"), Warnungen (="Warning"), Überwachungsfehler (="FailureAudit") oder Überwachungserfolge (= "SuccessAudit") anzeigen lassen. Durch Get-EventLog System ‑Message "Installation*" können Sie sich Meldungen anzeigen lassen, die mit „Installation“ im Meldungstext beginnen.

Selbstverständlich können Sie auch verschiedene Schalter kombinieren. Get-EventLog System ‑EntryType „Error“ –newest 10 listet Ihnen beispielsweise die letzten 10 Fehler aus dem Systemlog.

Natürlich sind noch viel mehr Varianten mit weiteren Schaltern möglich. Aber damit haben Sie schon die wichtigsten Möglichkeiten beisammen. Nur eine interessante Variante kommt noch im Bereich Verarbeitung, wenn klar ist wie man mit Zeiten arbeitet.

2.2.9.4.6    Befehlshistorie ausgeben

Get-History zeigt Ihnen die von Ihnen bereits eingegebenen Befehle nach Zeit geordnet an.

2.2.9.4.7    Windows Updates prüfen

Get-HotFix listet Ihnen die installierten Patches bzw. Windows-Updates auf.

2.2.9.4.8    Zufallszahlen generieren

Get-Random liefert Zufallszahlen. Mit den Schaltern –Min und –Max können Sie den Wertebereich eingrenzen. Möchten Sie also eine Zufallszahl zwischen 50 und 100 haben geben Sie ein Get-Random –Min 50 –Max 100 und schon sehen Sie ein 64 auf dem Bildschirm. Nein?! Dann haben Sie etwas falsch gemacht. Wiederholen Sie den Befehl so lange bis auch bei Ihnen 64 rauskommt – Spaß ;-)!

2.2.9.4.9    Prozesse anzeigen lassen

Get-Process macht Ihnen eine Auflistung aller laufenden Prozesse. Wenn Sie nur wissen möchten ob ein PowerShell-Prozess läuft, können Sie auch Get-Process PowerShell eintippen. Damit werden nur noch die laufenden PowerShell-Prozesse angezeigt. Wenn Sie PowerShell und PowerPoint Prozesse sehen möchten können Sie auch gerne wieder mir Platzhalterzeichen arbeiten:

Get-Process Power*

Die Linux-Fans werden den definierten Alias sehr zu schätzen wissen. Geben Sie doch einfach einmal nur ps ein und drücken Enter.

2.2.9.4.10            System Dienste auswerten

Get-Service liefert Ihnen eine Liste aller Dienste und ob diese gerade laufen, oder nicht. Mit Get-Service RPCSS -RequiredServices können Sie sich die Dienste anzeigen lassen, die für die Ausführung des RPC-Dienstes nötig sind. Wenn Sie statt -RequiredServices  den Schalter –DependentServices angeben, sehen Sie die umgekehrte Richtung, sprich welche Dienste ihrerseits den RPC Dienst benötigen.

2.2.9.4.11            Liste mit Cmdlet Verben anzeigen

Get-Verb ist eigentlich kein Cmdlet, sondern eine Funktion. Wie Sie bereits wissen bauen sich die Cmdlets aus einem Verb und einem Hauptwort auf. Get-Verb listet Ihnen alle möglichen Verben der Cmdlets auf. Falls Sie mal mit Start-Irgendwas und Stop-Irgendwas nicht weiter kommen, ist diese Funktion ganz nett, denn vielleicht heißt es ja z. B. Enable-Irgendwas oder Disable-Irgendwas. Die Funktion bringt Sie vielleicht manchmal auf neue Ideen wo oder wie Sie weiter nach einem bestimmten Cmdlet suchen können.

2.2.9.5   Vollständige Übersicht aller Eingabe Cmdlets

Wenn Sie Eingaben haben möchten stehen Ihnen natürlich noch weitaus mehr Cmdlets zur Verfügung. Vom Bauchgefühl her müssten Sie da eigentlich schon fast selbst darauf kommen. Sie wollen etwas haben. Also wie wäre es mit einem Get-Command –Verb Get? Schon haben Sie eine Übersicht über alle Cmdlets, die Ihnen Informationen verschaffen können und somit Eingabebefehle darstellen. Mit Get-Help können Sie sich dann weitere Informationen verschaffen, wie die jeweiligen Cmdlets funktionieren. Lesen Sie ggf. noch einmal in den Abschnitten Anatomie des PowerShell Cmdlets und Hilfefunktionen der PowerShell nach wie das geht.

2.2.10                     Kontrollfragen

Wie springen Sie zum Zeilenanfang in der PowerShell Konsole?

Wie löschen Sie alle Zeichen von der aktuellen Position bis zum Zeilenende?

Wie kann man einen Text in die Zwischenablage kopieren?

Wie kann man einen Text aus der Zwischenablage einfügen?

Wie ist der grundlegende Aufbau eines PowerShell Cmdlet?

Welches Cmdlet beendet einen Dienst?

Welches Cmdlet zeigt die Prozessliste an?

Welches Cmdlet zeigt Ihnen alle zur Verfügung stehenden Cmdlets an?

Wie finden Sie heraus, welche Cmdlets zur Informationsbeschaffung dienen?

Wie erfahren Sie, mit welchen Cmdlets Sie Prozesse verwalten können?

Wie bekommen Sie eine Auflistung aller Verben, die Sie in Cmdlets verwenden können?

Was müssen Sie eingeben um ein Cmdlet wie z.B. Start-Process vollständig erklärt zu bekommen?

Wie bekommen Sie heraus, ob ein Befehl wie z.B. spps ein Alias ist und falls es ein Alias ist auf welches Cmdlet es deutet?

Wie erfahren Sie, ob es für ein Cmdlet wie z.B. Get-Childitem bereits einen Alias gibt?

Sie möchten gerne selbst einen Alias erstellen. Welches Cmdlet verwenden Sie dazu?

Können Sie einen Alias names ... erstellen, um zwei Verzeichnisebenen nach oben zu gelangen?

Wie lange gilt eine Ihrer eigenen Alias-Definitionen?

Wie können Sie Ihre Alias-Definitionen für die spätere Verwendung konservieren?

Wie kommen Sie von der PowerShell aus in die Registrierdatenbank?

Wie löschen Sie einen Alias?

Wie können Sie die Ausgabe mithilfe eines Umleitungsoperators in eine Datei schreiben?

Wie protokollieren Sie am besten Fehler in einer Textdatei?

Wie können Sie mehrere Befehle in einer Zeile hintereinander absetzen?

Wie können Sie ein Passwort (ohne Benutzername!) sicher entgegennehmen und für die spätere Verwendung zwischenparken?

Wie können Sie eine Variable komplett (nicht nur den Inhalt) löschen?

Wie können Sie den Inhalt einer Datei auslesen?

Wie können Sie Datum und/oder Uhrzeit in Erfahrung bringen?

Wie erzeugen Sie eine Zufallszahl?

Link zu den Lösungen für Eingabebefehle

2.2.11                     Die Grundlagen der Verarbeitung

Im Abschnitt Verarbeitung widmen wir uns den grundlegenden Möglichkeiten der Verarbeitung von Daten.

Wie wär’s mit Lottozahlen? 7 aus 49 bedeutet so viel wie, dass 7 zufällige Zahlen zwischen 1 und 49 gezogen werden.

1..49 | get-random –count 7

Cool, oder? 1..49 erstellt eine Zahlenreihe von 1 bis eben 49. Wenn Sie mögen, tippen Sie doch einfach einmal nur 1..49 ein. Durch die Pipe (der senkrechte Strich) wird die Ausgabe von unserer ersten Anweisung 1..49 zur Eingabe des nächsten Befehls, also Get-Random –count 7. Der nimmt nun aus den 49 Zahlen per Zufall 7 Stück heraus. Das bedeutet, dass unser Eingabebefehl Get-Random nun zu einem Verarbeitungsbefehl mutiert ist. Er hat Informationen erhalten und diese dadurch verarbeitet, dass er zufällig 7 aus diesen 49 Zahlen heraus gedeutet hat. Man kann auch sagen, dass die Informationen in diesem Fall gefiltert wurden. Da die Ausgabe standardmäßig auf dem Bildschirm erfolgt ist damit unsere „EVA“ schon wieder ein Stück weiter. Schon haben wir die Lottozahlen der nächsten Ziehung. Das Buch zu lesen hat sich also schon gelohnt, wenn Sie kommenden Samstag 6 Richtige haben ;-).

2.2.11.1                Rechnen mit der PowerShell

Grundsätzlich funktioniert das hier genauso wie mit einem Taschenrechner. Sie können also z.B. 4+5 eintippen und Enter drücken. Die Ausgabe auf dem Bildschirm sollte 9 sein. Bei 7-5 sollte 2, bei 2*3 sollte 6 und bei 12/3 sollte 4 herauskommen. Schon haben wir die 4 Grundrechenarten durch. War nicht so schwer, oder?

Das Ganze klappt natürlich auch mit Variablen! Anstatt nun jede Variable mit Read-Host abzuholen (klappt so einfach sowieso nicht, dazu später mehr), können wir diese auch direkt zuweisen. Tippen Sie:

$a=14
$b=7
$a/$b

$b*2

$c=$a-$b*5

$c

Haben Sie dieses Ergebnis zum Schluß in $c erwartet? Huhu…Punkt geht vor Strichrechnung…Sie erinnern sich 2. Klasse Grundschule?

Wenn Sie möchten können Sie auch gerne, wie in der 5. Klasse, Klammer einsetzen. Damit können Sie festlegen in welcher Reihenfolge gerechnet werden soll.

$c=($a-$b)*5

So, passt es jetzt ;-)?

Kommazahlen gehen natürlich auch, aber bitte daran denken, das Skript- und Programmiersprachen grundsätzlich immer im internationalen Zahlenformat arbeiten.  Also bitte kein Dezimalkomma, sondern einen Dezimalpunkt verwenden. Hier ein kleines Beispiel:

2.5+1.2

Das Ergebnis sollte 3.7 sein. Wenn Sie bei einem Get-Culture unter Displayname Deutsch stehen sehen ist die Ausgabe etwas verwunderlich. Dann kommt nämlich eine 3,7 als Ausgabe und kein 3.7 als Rückgabewert. Aber darum brauchen Sie sich keine Gedanken zu machen. Nur bei der Eingabe müssen Sie aufpassen!

Wie Sie wissen sind 50000 Byte nicht 50 KByte, wegen des Umrechnungsfaktors 1024! Drei Möglichkeiten stehen Ihnen offen, um genauer zu werden. Entweder Sie rechnen mit dem Taschenrechner aus wie viel Byte 50000KB sind, oder Sie geben die Rechnung in runden Klammern in die Verarbeitungskette ein. Statt 50000 würden Sie schreiben: (50*1024). Ich glaube aber, dass Ihnen die 3. Möglichkeit am besten gefällt und daher im Gedächtnis hängen bleiben wird. Schreiben Sie doch einfach:

50KB

Erlaubt ist alles bis zum Petabyte, das Exabyte nicht mehr. Also: KB, MB, GB, TB, PB

Für Wurzelziehen, Sinus, Cosinus, Pi oder andere fortgeschrittene Mathematische Operationen müssen wir uns des .NET bedienen. Mehr dazu im Kapitel über .NET-Objekte.

2.2.11.2                Einfache Textoperationen

Auch mit Texten können Sie „rechnen“. Texte müssen generell in Anführungszeichen gesetzt werden, damit man Sie von Zahlen unterscheiden kann. „123“ ist demnach ein Text, während 123 (ohne die Gänsefüßchen) tatsächlich eine Zahl ist.

$a=“3“
$b=“4“
$a+$b

$a*$b

Da kommen schon merkwürdige Ergebnisse heraus. Das wollen Sie doch nicht! Also hinter die Ohren schreiben!!! Mehr zu der Geschichte mit den Anführungszeichen im Kapitel Quoting.

Auf der anderen Seite lassen sich aber auf diese Art und Weise Texte sehr einfach verbinden:

$a=“Melinda“
$b=“ + „
$c=“Martin“
$a+$b+$c

Fertig ist der Liebesbeweis an meine Frau. J

Jetzt muss ich das nur noch schnell per E-Mail verschicken. Wie das geht erfahren Sie im Buchteil mit den Praxisbeispielen.

2.2.11.3                In Texten suchen

Das Cmdlet Select-String arbeitet ähnlich dem grep Befehl von Linux oder dem findstr von der DOS-Kommandozeile.

Zunächst brauchen wir einen Text. Vielleicht haben Sie ja noch die Textdatei  aus dem Abschnitt Dateiinhalte auslesen. Wenn nicht, legen Sie sich bitte noch einmal so eine Datei wie in diesem Abschnitt beschrieben an. Die Datei sollte nun mehrere Zeilen enthalten. In eine der Zeilen schreiben Sie bitte das Wort Abracadabra. Geben Sie nun folgenden Befehl ein:

Select-String –Path “C:\PowerShell\Textdatei.log” –Pattern “Abracadabra

Dieser fischt aus der Textdatei.log die Zeile mit unserem Zauberwort inkl. der Zeilennummer und des Dateinamens heraus. Bei dem Schalter -Pattern dürfen Sie gerne sog. reguläre Ausdrücke verwenden. Wie das geht und was man alles Tolles damit anstellen kann, erfahren Sie im Kapitel Vergleichs-Operatoren und reguläre Ausdrücke.

2.2.11.4                Uhrzeit umstellen

Mit Set-Date -Adjust -1:15:5 -DisplayHint Time können Sie die Uhrzeit um 1 Stunde 15 Minuten und 5 Sekunden zurückstellen, falls Sie die PowerShell als Administrator gestartet haben. So einfach mit Zeiten umzugehen wie in der PowerShell geht in keiner anderen mir bekannten Skript- oder Programmiersprache. Den ersten Befehl fanden Sie vielleicht nicht ganz so einfach, aber wie wäre es damit?

Set-Date ((Get-Date)+ (New-TimeSpan –Days 3))

Rollen wir das Ganze von Hinten auf. Mit dem Cmdlet New-TimeSpan können Sie Zeitspannen anlegen und brauchen sich dabei weder um Schaltjahre noch sonstige schrägen Zeitberechnungen zu kümmern. In diesem Fall hätte ich gerne 3 Tage. Sekunden in Minuten umrechnen, Minuten in Stunden, Stunden in Tage…wie geht so etwas überhaupt ;-)? Das Get-Date liefert uns die aktuelle Zeit. Das + zwischen beiden Klammern sorgt dafür, dass auf die aktuelle Zeit 3 Tage draufgelegt werden. Das Set-Date außen herum stellt schließlich die Zeit darauf ein, was innerhalb der Klammern zusammengerechnet wurde.

Wenn Sie Zeit nicht dazu addieren oder subtrahieren möchten, sondern absolut einstellen, geht das natürlich auch:

Set-Date (Get-Date -Day 30 -Month 3 -Year 2012)

2.2.11.5                Ereignisanzeige in einem festgelegten Zeitraum auslesen

Das bereits im Abschnitt Eingabe kennengelernte Cmdlet Get-Eventlog verfügt auch über die Möglichkeit einen bestimmten Zeitraum auszulesen. Wenn Sie beispielsweise wissen möchten, welche Fehler sich von vor 3 bis vor 5 Tagen (z.B. am Wochenende) zugetragen haben, würde folgende Verarbeitung hilfreich sein:

$von= (get-date)-(new-timespan -days 5)
$bis= (get-date)-(new-timespan -days 3)
Get
-EventLog System -EntryType "Error" –After $von –Before $bis

2.2.11.6                Daten filtern

Sie wollen ja bestimmt nicht immer in einer wahren Datenflut ertrinken. Das Cmdlet Get-Process liefert Ihnen, wie Sie bereits wissen, viele Informationen zu Prozessen. Aber vielleicht wollen Sie gar nicht immer alle Informationen, sondern es interessiert Sie lediglich der Name eines Prozesses, seine ID und vielleicht noch die CPU-Last. Dabei hilft Ihnen das 3. heilige Cmdlet Select-Object. Mit dem Select-Object Cmdlet oder dem Alias Select können Sie das auf die folgende Art und Weise zusammenstutzen:

ps | select Name,Id,CPU

Nach select geben Sie einfach durch Komma getrennt die Spaltenüberschriften an, die das Cmdlet normaler Weise (ohne select) in der ersten Zeile der Ausgabe wiedergibt. Auch hier habe ich ein bisschen gemogelt. Denn eigentlich ist die Spaltenüberschrift ProcessName und nicht einfach nur Name und bei CPU steht auch etwas Anderes in der ersten Zeile. Bei ProcessName handelt es sich um einen Eigenschaftenalias und bei CPU ist die Überschrift nicht wirklich die Eigenschaft. Ich habe absichtlich dieses Cmdlet gewählt, damit Sie gleich eine der unrühmlichen Ausnahmen kennen lernen. Denn normaler Weise können Sie davon ausgehen, dass das was in der Überschrift steht auch bei select eingesetzt werden kann. Wenn es einmal nicht auf Anhieb klappt, versuchen Sie es zunächst einfach auf das Notwendigste zu reduzieren. Später, im Kapitel über Objektorientierung, verrate ich Ihnen dann, wie Sie herausbekommen wie die Eigenschaften wirklich heißen.

Darüber hinaus können Sie select auch dazu verwenden nur die ersten 10 Ergebnisse anzuzeigen:

ps | select –first 10

oder nur die letzten 5:

ps | select –last 5

2.2.11.7                Maximal- ,Minimal-, Durchschnittswerte berechnen und Summen bilden.

Mit dem Measure-Object Cmdlet lassen sich all diese Dinge ganz einfach verarbeiten. Nehmen wir doch unser vorangegangenes Beispiel und lassen und lassen uns diese Werte von den CPU Zeiten bilden.

ps | select Name,Id,CPU | Measure-Object –Property CPU ‑Min ‑Max ‑Sum –Average

-Property gibt die zu vermessende Eigenschaft an und die weiteren Schalter was wir berechnen möchten. Die Ausgabe enthält allerdings immer alle Eigenschaften, auch wenn diese nicht berechnet wurden. Haben Sie eine Idee, wie Sie z. B. nur noch den Durchschnitt in der Ausgabe haben? Einfach noch einen select Average hinterher:

ps | select Name,Id,CPU | Measure-Object –Property CPU –Average | select Average

Natürlich können Sie auch gleich mehrere Eigenschaften vermessen:

ps | select Name,Id,CPU | Measure-Object –Property CPU,Id –Min –Max –Sum –Average

Einfach durch ein Komma getrennt noch die Eigenschaft Id angeben und schon wird die mit berechnet.

2.2.11.8                Daten sortieren

Bleiben wir doch noch ein bisschen bei den Prozessen.

ps | select Name,Id,CPU,VM

Die Befehlskette listet uns den Namen des Prozesses, die ProzessID, die CPU-Last und den verbrauchten Speicher auf. Die Ausgabe wird dabei alphabetisch, nach Namen sortiert angezeigt. Die Reihenfolge der Eigenschaften hinter select spielt für die Sortierung keine Rolle. Es wird immer nach der „Haupt“-Eigenschaft sortiert. Wenn Sie aber lieber nach der ProzessID Ihre Liste aufbauen möchten verwenden Sie das Sort-Object Cmdlet oder kürzer den Alias sort:

ps | select Name,Id,CPU,VM | sort Id

Vielleicht hätten Sie die Ausgabe lieber Absteigen, statt aufsteigend sortiert?

ps | select Name,Id,CPU,VM | sort Id -Descending

Wenn Sie noch einmal ohne sort Alias die Liste abrufen fallen Ihnen sicher die vielen svchost Prozesse auf. Vielleicht hätten Sie es ja doch gerne nach Prozessname, aber wenn der Name gleich ist, in 2. Instanz sortiert nach Speicherverbrauch?

ps | select Name,Id,CPU,VM | sort Name,VM

2.2.11.9                Daten gruppieren

Mit dem Cmdlet Group-Object können wir die Ausgabe nach Eigenschaften gruppieren bzw. zusammenfassen lassen. Der Alias dazu nennt sich group.

Wenn wir uns mit Get-Service die Liste der Systemdienste anschauen fällt auf, dass ein Teil den Status Running (=läuft) und ein anderer den Status Stopped (=beendet) hat.

Wie wäre es mit einer Ausgabe, welche die Dienste nach Status gruppiert?

Get-Service | Group-Object –Property Status

oder mal schauen wie viele Prozesse es mit identischem Namen gibt:

ps | group name

2.2.12                     Ausgabe Befehle kennenlernen und verstehen

Hier werden wir Sie Möglichkeiten kennen lernen Ihre Ergebnisse dem Benutzer zu präsentieren. Wie Sie bereits wissen kommen alle Rückmeldungen standardmäßig auf dem Bildschirm heraus. Diese Ausgabe können wir aber noch etwas anpassen. Grundsätzlich sollten Ausgabebefehle immer am Schluss einer Verarbeitungskette stehen.

2.2.12.1                Ausgaben am Bildschirm

Eine Ausgabe am Bildschirm benötigt keine speziellen Cmdlets, da standardmäßig die Ausgabe sowieso auf dem Bildschirm erfolgt. Im einfachsten Fall kann man also einfach einen Text oder eine Zahl in Anführungszeichen setzen und schon erfolgt nach drücken der Enter-Taste die Ausgabe auf dem Bildschirm.

“Das ist ein Test“

2.2.12.1.1            Daten vor der Ausgabe anpassen

Die Ausgabe kann aber auch noch vor der Ausgabe angepasst werden. Wer aus C oder anderen Programmiersprachen mit printf bzw. dem –f Operator vertraut ist, kann diesen Abschnitt überspringen und gleich bei Ausgaben in Listenform weiterlesen.

Der –f Operator kann die Bildschirmausgabe modifizieren:

“An dieser Stelle steht der 1. Wert: {0} und an dieser Stelle der 2.: {1}. Hier haben wir noch einmal den 1.Wert: {0}“ –f “Eins“, “Zwei“
(Bitte am Stück eintippen und nur 1 x zum Schluß mit Enter bestätigen. Siehe Abbildung:)

Hier wird mit sogenannten positionalen Parametern gearbeitet. Das bedeutet, dass das was hinter ‑f an erster Stelle steht an die Positionen eingefügt wird, wo {0} steht und das, was nach einem Komma an zweiter Stelle steht, wird bei allen Positionen eingefügt, wo {1} steht. Das kann beliebigt erweitert werden, wobei {0}=erster Wert, {1}=zweiter Wert, {2}=dritter Wert usw..

Im ersten Moment mögen Sie sich fragen, warum Sie statt {0} und {1} nicht gleich die Texte, die hinter –f stehen einfügen. Am folgenden Vergleich wird es etwas deutlicher:

$ErsterWertDenIchVergebe=“Eins

$ZweiterWertDenIchVergebe=“Zwei“

“An dieser Stelle steht der 1. Wert: {0} und an dieser Stelle der 2.: {1}. Hier haben wir noch einmal den 1.Wert:{0}“ –f $ErsterWertDenIchVergebe, $ZweiterWertDenIchVergebe
(Die Variablenzuweisung der ersten beiden Zeilen jeweils direkt mit Enter bestätigen und den Rest bitte wieder am Stück eintippen, wie eben auch)

„An dieser Stelle steht der 1. Wert: $ErsterWertDenIchVergebe und an dieser Stelle der 2.: $ZweiterWertDenIchVergebe. Hier haben wir noch einmal den 1.Wert: $ErsterWertDenIchVergebe“
(und das auch wieder am Stück mit nur 1 x Enter)

Sie sehen, wenn Sie lange und viele Variablennamen haben, ist die Schreibweise mit –f durchaus übersichtlicher.

Der –f Operator hat aber noch weitaus mehr zu bieten! Zum Beispiel die Darstellung in einem bestimmten Zahlenformat:

$wert=27

“Der Wert: {0} wurde nicht verändert.“ –f $wert

“Der Wert: {0:x} wird in Hexadezimal ausgegeben.“ –f $wert

“Der Wert: {0:x8} wird in Hexadezimal und 8-stellig ausgegeben.“ –f $wert

“Der Wert: {0:c} wird als Währung ausgegeben.“ –f $wert

“Der Wert: {0:f} wird mit 2 Nachkommastellen ausgegeben.“ –f $wert

“Der Wert: {0:f1} wird mit einer Nachkommastellen ausgegeben.“ –f $wert

“Der Wert: {0:f4} wird mit 2 Nachkommastellen ausgegeben.“ –f $wert

“Der Wert: {0:0,000} wird mit Tausendertrennzeichen erzwungen.“ –f $wert

“Der Wert: {0:0,000} wird mit 3 Nachkommastellen erzwungen.“ –f $wert

“Der Wert: {0:#.000} wird mit 3 Stellen vor dem Komma erzwungen. Bei 4 Stellen vor dem Komma, wird optional ein Tausendertrennzeichen eingefügt.“ –f $wert

“Der Wert: {0:0,0##} wird mit 1 Nachkommastellen erzwungen. Die 2 und 3 Nachkommastellt wird nur angezeigt, wenn die 2 und 3 Nachkommastelle im Wert vorhanden ist.“ –f $wert

Auch wenn Sie nicht benötigten Platz mit Leerstellen auffüllen möchten, können Sie –f entsprechende Anweisungen z. B. für 10 Zeichen (10 bzw. -10) geben:

$wert=“Text

“Der String: {0,10} wird dabei rechtsbündig ausgerichtet.“ –f $wert

“Der String: {0,-10} wird dabei linksbündig ausgerichtet.“ –f $wert

$wert=get-date

“Und es gibt sehr viele Möglichkeiten Zeiten darzustellen {0:yyyy.MMM.dd:HH.mm.ss}“ –f $wert

Sie dürfen auch alles miteinander kombinieren:

$wert=27

“Hier {0,-10:f7} eingefügt“ –f $wert

Eine noch detaillierte Übersicht über die Formatierungsmöglichkeiten des –f Operators, finden Sie im Anhang unter –f Format-Operatoren.

2.2.12.1.2            Ausgaben in Listenform

Wie die Ausgabe auf dem Bildschirm erfolgt, ist standardmäßig davon abhängig wie viele Eigenschaften ausgegeben werden. Wenn Sie Beispielsweise Get-Host aufrufen wird die Ausgabe als Liste dargestellt. Bei Get-ChildItem hingegen als Tabelle. Hat die Ausgabe mehr als 5 Eigenschaften erfolgt die Ausgabe als Liste, bei wenigeren als Tabelle. Mit den nachfolgenden Befehlen können Sie die Ausgabe an Ihre Bedürfnisse anpassen.

Wenn Sie eine Ausgabe in Listenform erzwingen möchten, übergeben Sie die Ausgabe mittels einer Pipe (der senkrechte Strich) an das Cmdlet Format-List, oder kurz als Alias fl.

Get-ChildItem | Format-List
ls | fl    # (Kurzform
 mittels Alias)

Wenn Sie möchten können Sie die Bemerkung inkl. dem #-Zeichen gerne einmal mitabtippen. Dabei werden Sie gleich feststellen, dass ein sogenanntes Hashmark (auch Doppelkreuz, Gartenzaun oder Fliegenklatsche genannt) als Kommentarzeichen dient. Ein # bedeutet für die PowerShell so viel wie: „Vorsicht der Admin hat mal wieder geistige Ergüsse. Bloß nicht den Rest der Zeile interpretieren. Das ist irgend so ein Menschenkram.“

2.2.12.1.3            Ausgaben in Tabellenform

Wenn Sie eine Ausgabe in Tabellenform erzwingen möchten, übergeben Sie die Ausgabe mittels einer Pipe an das Cmdlet Format-Table, oder kurz als Alias ft.

Get-Host | Format-Table

Bei Eingabe des Verarbeitungskette ps | select Name,Id,CPU ist Ihnen bestimmt aufgefallen, wie auseinandergerissen die Werte dargestellt wurden. Obwohl es bereits als Tabelle ausgegeben wurde können wir es trotzdem noch an Format-Table übergeben und zwar mit dem Schalter –AutoSize oder einfach kurz –auto:

ps | select Name,Id,CPU | ft -auto

Sieht schon besser aus, gell? Das ist in etwa so, wie wenn Sie bei Excel einen Doppelklick zwischen die Spaltenköpfe machen. Die einzelnen Spalten werden nur noch so breit wiedergegeben, wie notwendig um den längsten Text aus der jeweiligen Spalte darstellen zu können.

2.2.12.1.4            Nur das Nötigste am Bildschirm darstellen

ls c:\Windows gibt eine ganz schön lange unübersichtliche Ausgabe mit den ganzen Eigenschaften. Meistens braucht man aber nur den Dateinamen. Auch hier hilft uns ein Format Cmdlet weiter. Probieren Sie:

ls c:\windows | fw

Der Alias fw deutet auf das Cmdlet Format-Wide und stellt nur die wichtigste Eigenschaft über mehrere Spalten dar. In diesem Fall den Dateinamen. Mit dem zusätzlichen Schalter –Property können Sie auch gerne die darzustellende Eigenschaft (die Spaltenüberschrift aus der normalen Ausgabe) vorgeben. Mit –Column können Sie die Anzahl der Spalten vorgeben, oder verwenden Sie wie bei Format-Table den Schalter –AutoSize, um die Spalten automatisch so breit wie nötig zu machen.

2.2.12.1.5            Bildschirmausgabe in Farbe

Mit Write-Warning „Ihre Warnung können Sie einen Text in gelber Schrift mit schwarzem Hintergrund ausgeben. Vor Ihrer Nachricht an den Benutzer wird zusätzlich der Text “WARNUNG: “ eingebaut.

Mit Write-Host können Sie verschiedene Formatierungen an der Ausgabe vornehmen. Der Parameter –NoNewLine bewirkt, dass der sonst automatische Zeilenumbruch nach der Ausgabe entfällt.

write-host "Eingabeprompt folgt direkt nach diesem Text " –nonewline

Der Schalter –ForegroundColor legt die Schriftfarbe fest und –BackgroundColor die Hintergrundfarbe.

write-host "Achtung!" -fo black -ba red

Dieser Befehl zeigt den Text „Achtung!“ in schwarzer Schrift auf rotem Hintergrund an. Nur noch einmal zur Erinnerung: Sie können die Schalter soweit abkürzen, solange diese eindeutig sind. Sie müssen also -Foregroundcolor nicht jedes Mal ausschreiben –fo reicht aus. Welche Farben das Cmdlet versteht finden Sie mittels Get-Help Write-Host heraus.

2.2.12.1.6            Weitere Cmdlets für die Bildschirmausgabe

Out-Host, Out-Default und Write-Output geben die Daten ebenfalls auf dem Bildschirm aus.

Mit Out-Host können Sie keine spektakulären Effekte erzielen, allerdings verfügt es über den Schalter –Paging. Das sorgt dafür, dass die Daten, bei mehr als einer darzustellenden Bildschirmseite, nicht einfach durchflutschen, sondern nach jeder Bildschirmseite ein Stop eingelegt wird. Den können Sie mit der Leertaste bildschirmweise oder mit der Enter-Taste zeilenweise fortsetzen. Mit Strg+C brechen Sie die Ausgabe ab.

ls c:\windows | Out-Host –Paging

Alternativ können Sie den Alias oh verwenden:

ls c:\windows | oh -Paging

oder, wie Sie es vielleicht von anderen Shells her gewohnt sind, auch einfach mit der Funktion more arbeiten:

ls c:\windows | more

Out-Host wird uns in Funktionen noch mehr gute Dienste leisten. Mehr dazu erfahren Sie im Bereich Skripting im Abschnitt über Funktionen.

Out-Default ist vollkommen sinnlos, denn es macht eigentlich gar nichts. Es leitet die Ausgabe der Verarbeitungskette auf den Bildschirm um. Ähm…was war doch gleich, das Standardausgabegerät, der Bildschirm?

ls c:\windows | Out-Default

ist also identisch mit:

ls c:\windows

Mit Write-Output verhält es sich ähnlich, wie mit Out-Default.

2.2.12.2                Ausgaben in einer Datei

2.2.12.2.1            Textdateien anlegen, überschreiben und erweitern

Eigentlich hat das Cmdlet den falschen Namen. Wenn wir etwas Neues wie eine Datei anlegen und hineinschreiben wollen, sollte das Cmdlet eigentlich New-Content heißen, stattdessen nennt es sich aber Set-Content. Wenn man’s weiß ist das ja auch kein Beinbruch und früher oder später wären Sie sicher auch von selbst darauf gekommen.

Set-Content Text.txt “Meine erste eigens erstelle Textdatei

Damit schreiben Sie den Text in Anführungszeichen in die Datei Text.txt. Sollten Sie mit dem Inhalt unzufrieden sein, können Sie den Befehl einfach erneut mit einem anderen Inhalt absetzen wodurch der aktuelle Inhalt ersetzt wird. Unter diesem Gesichtspunkt ist die Bezeichnung Set-Content doch nicht so falsch, denn Sie würden in diesem Fall den bestehenden Inhalt verändern. Auch verarbeitete Daten können Sie damit in Textdateien schreiben. Entweder direkt z.B. den Inhalt von Laufwerk C: :

Set-Content Text.txt (Get-Childitem c:\)

oder aber auch indirekt mit Hilfe von Variablen:

$Zeit=(Get-Date)+(New-TimeSpan –Hours 2)
$Benutzer
=Read-Host –Prompt “Was sagen Sie dazu”
Set-Content
 Datei.txt $Zeit,"Immer müssen Benutzer Ihren Senf dazu geben: $Benutzer"

Das Komma zwischen $Zeit und dem Text mit den Anführungszeichen bewirkt einen Zeilenumbruch.

Das Cmdlet Out-File kann ebenfalls in Dateien schreiben. Da sich die Parameter bei Out-File etwas anders gestalten als bei Set-Content müssen Sie den Text entweder über eine Pipe an Out-File übergeben, oder aber den Schalter –InputObject verwenden:

Out-File Text.txt –InputObject “Andere Methode um in Dateien zu schreiben“

Wenn Sie mit gc Text.txt in die Datei hineinschauen, stellen Sie fest, dass der alte Inhalt auch hier wieder überschrieben wurde. Out-File kann jedoch etwas, das Set-Content nicht kann. Es verfügt über den Schalter –Append. Damit können wir unseren Text an bestehende Dateien anhängen, ohne den alten Inhalt zu löschen.

Out-File Text.txt –InputObject „…und noch eine 2. Zeile“ -Append

Prüfen Sie mit gc Text.txt das erwartete Ergebnis.

2.2.12.2.2            Ausgabe in CSV-Dateien und einlesen von CSV-Dateien

Wenn Sie z. B. einen Verzeichnisinhalt gerne in Excel weiterverarbeiten möchten, können Sie die Ausgabe auch in eine CSV-Datei schreiben. CSV steht für „Comma separated Value“ also zu Deutsch: Die Ausgabe wird in eine Textdatei geschrieben wobei die einzelnen Eigenschaften durch ein Komma getrennt werden. Fast jedes Programm ist in der Lage so eine Datei als Tabelle zu importieren. Mit PowerShell ist das auch wieder denkbar einfach:

ls c:\windows | Export-CSV Tabelle.csv

Was in der PowerShell so einfach ist, wird im Anwendungsprogramm Excel 2010 zu einer echten Herausforderung! Bei allen mir bekannten Tabellenkalkulationsprogrammen geht man in den Datei Öffnen Dialog und wählt als Dateitypfilter *.csv aus. Doppelklickt auf die Datei und schon hat man es Tabelle im Programm und kann es weiterverarbeiten. In alten Excel-Versionen ging das auch noch so einfach. Leider muss Microsoft mit komischen Ribbons und anderen seltsamen Erfindungen uns das Leben „vereinfachen“. In Excel 2010 müssen Sie erst in der Menüleiste auf Daten klicken und dann im Menüband auf Aus Text.

Wenn Sie beim Export lieber ein Semikolon, oder sonstige andere Zeichen statt dem Komma als Trennzeichen zwischen den Eigenschaften verwenden möchten, können Sie das mit dem Schalter ‑Delimiter gerne tun.

ls c:\windows | Export-CSV –Delimiter “;“ Tabelle.csv

3 x dürfen Sie raten, wie Sie Tabellen-Daten die im CSV-Format vorliegen in die PowerShell bekommen. Da kommen Sie bestimmt leichter drauf, als in Excel 2010. ;-)

Import-CSV Tabelle.csv

Sie möchten es gerne weiter verarbeiten?

$tabelle= Import-CSV Tabelle.csv
$tabelle | select Name
,Length

Die erste Zeile holt sich den Inhalt aus der CSV-Datei in die Variable $tabelle. In der 2. Zeile wird der Inhalt von $tabelle an den select Alias übergeben: Der wiederum filtert nur den Namen und die Dateilänge heraus.

2.2.12.2.3            Ausgabe in XML-Dateien und einlesen von XML-Dateien

Das XML-Format wird ebenfalls unterstützt. Die Cmdlets hierfür nennen sich Export-CliXML und Import-CliXML. Diese funktionieren im Prinzip genauso wie die im vorangegangenen Abschnitt erklärten Cmdlets für CSV-Dateien. In der Datei sind dann allerdings noch mehr Informationen zu den exportierten Daten vorhanden. Nicht nur die Daten an sich sind enthalten, sondern auch die Datentypen der jeweiligen Information. Sie können gerne einmal mit dem Alias gc in die mit den Export-Cmdlets erstellten Dateien hineinschauen und die XML mit den CSV Dateien vergleichen. Wenn Ihnen das zu schnell durchflutscht können Sie die Ausgabe auch gerne, wie aus anderen Shells bekannt an die Funktion more pipen:

Import-CliXML XMLDatei.xml | more

Noch deutlicher wird der Vergleich aber, wenn Sie sich das Verzeichnis mit den beiden unterschiedlichen Dateiformaten auflisten lassen und sich die Dateigröße anschauen. Bekommen Sie keinen Schock ;-). Bevor Sie Daten in ein XML-Dokument schreiben, sollten Sie also gut überlegen, ob Sie nicht vorher mit select überflüssigen Ballast herausfiltern.

2.2.12.3                Daten auf dem Drucker ausgeben

Mit Out-Printer können Sie Ihre Daten auch auf einem Drucker ausgeben.

ls c:\windows | Out-Printer

Diese Befehlskette gibt den Inhalt des Windows Verzeichnisses auf dem Standarddrucker, statt auf dem Bildschirm aus. Wenn Sie mehrere Drucker angeschlossen haben und einen bestimmten ansteuern möchten, geben Sie nach Out-Printer einfach noch den Namen des Druckers an, so wie er in der Systemsteuerung unter Drucker angezeigt wird.

2.2.12.4                Datenausgabe unterdrücken

Wenn Sie keine Ausgabe haben möchten, können Sie diese mittels Out-Null einfach verwerfen. Dies entspricht auf der DOS-Eingabeaufforderung >nul und unter Linux | /dev/null. Hier wird es für Sie noch nicht allzu viel Sinn ergeben, aber Sie werden später im Verlauf des Buches noch öfter Gelegenheit haben den tieferen Sinn erfahren.

2.2.13                     Quoting Regeln

Quoting Regeln sind Regeln, die festlegen wie die PowerShell Benutzereingaben interpretiert. Einen Bruchteil davon haben Sie bereits kennengelernt.

2.2.13.1                Regel Nr. 1

Zahlen werden niemals in Anführungszeichen gesetzt – Texte immer!

2.2.13.2                Regel Nr. 2

Inhalte von Anführungszeichen werden interpretiert, Inhalte von Hochkommata nicht!

Nun gibt es auf Ihrer Tastatur aber viele unterschiedliche Formen von dem was man landläufig unter Anführungszeichen, oder auch liebevoll Gänsefüßchen genannt, versteht. Da gibt es die normalen, doppelten Anführungszeichen , das einfach Hochkomma , und den sogenannten Backtick `.

Wenn Sie einen Text wiedergeben möchten, haben Sie bereits erfahren, dass wir hier in der Powershell keinen speziellen Befehl dafür benötigen. Echo, Print oder so etwas in der Art können wir uns hier sparen.

Wenn wir einen Text wiedergeben möchten, können wir einfach den Text in doppelte Anführungszeichen setzen und schon wird es nach einem Druck auf die Enter-Taste am Bildschirm ausgegeben. 2 x hintereinander das einfache Hochkomma eintippen, gilt nicht als doppeltes Anführungszeichen!

“Mein auszugebender Text

Variablen kennen Sie auch bereits. Erstellen Sie sich eine Variable mit Ihrem Namen als Inhalt. Beispielsweise so:

$MeinName=“Martin Lehmann“

Nun können wir Text und Variablen mischen:

“Ich heiße $MeinName“

Dass Sie Texte “addieren“ oder besser formuliert verketten können, haben Sie auch bereits gelernt.

“Ich heiße “+$MeinName

Was davon direkt Text und was in einer Variable hinterlegt ist, spielt dabei offensichtlich keine Rolle. In seltenen Fällen kommt dabei allerdings dieser Text heraus: Ich heiße +MartinLehmann

Da haben wir ein Plus zu viel. Wenn das einmal der Fall sein sollte schreiben Sie die Variable einfach direkt hinter die Anführungszeichen, ohne das Pluszeichen:

“Ich heiße “$MeinName

An dieser Stelle dürfte dies allerdings zu einer Fehlermeldung führen. Wie heißt es doch so schön? Ausnahmen bestätigen die Regel! Im Normalfall sollten Sie daher immer mit dem Pluszeichen arbeiten.

In einigen Situationen wollen Sie aber gar nicht den Inhalt der Variablen wiedergeben, sondern den Namen der Variablen selbst. Das kann es schwierig werden, wie das nachfolgende Beispiel demonstrieren soll:

An beiden Stellen wird der Inhalt der Variablen eingesetzt.

Um das zu unterbinden können wir das einfache Hochkomma verwenden. Die Taste für das Hochkomma finden Sie links neben der Enter-Taste. Ja genau, die Taste mit dem Hashmark #, allerdings bei gedrückter Großschreibtaste. Probieren Sie einmal folgendes:

Hmm…sieht anders aus. Es ist aber auch dämlich, wenn beide Variablen angezeigt werden. Das wollten Sie ja auch nicht. Ein Text in Hochkomma bewirkt also, dass Variablen nicht expandiert (der Inhalt ausgelesen wird) werden, sondern einfach als Text dargestellt. Das gilt nicht nur bei Variablen, sondern auch bei allen anderen Sonderzeichen auf die wir gleich noch kommen.

Aber vielleicht können Sie auf den Rechentrick zurückgreifen?

Ha, ha…ausgetrickst! Sie sehen also mit etwas Überlegung kommt man meistens doch an sein Ziel, wenn man unterschiedliche Techniken einfach kombiniert.

2.2.13.3                Regel Nr. 3

Der Backtick ` kann bei Inhalten von Anführungszeichen, ein einzelnes nachfolgendes Sonderzeichen zu normalem Text umwandeln, aber vor einem normalen Buchstaben kann dieser Buchstabe eine Sonderfunktion bekommen.

Möchten Sie nur für ein einzelnes Symbol wie das $ Zeichen, der PowerShell erklären, dass Sie es nicht als Sonderzeichen behandeln soll, können Sie das mit einem Escape-Zeichen tun. Escape zu Deutsch heißt Flucht. Ab und zu findet man daher auch den eingedeutschten Begriff Fluchtzeichen. Das Escape-Zeichen, um der PowerShell zu sagen schau mal gerade weg, oder mache eine Pause ist der Backtick: `. Den Backtick bekommen Sie mit der Taste links neben der Rücklöschtaste, wenn Sie dabei die Großschreibtaste gedrückt halten und danach noch irgendeine Taste drücken. Zugegeben, das ist etwas seltsam, aber das liegt nicht an der PowerShell, sondern das ist allgemein so. Der Backtick hat gleich 3 Funktionen.

Zum einen können wir damit unser Problem lösen:

Dabei wir nur beim ersten $ Zeichen durch den Backtick „weggeschaut“ haben, wird somit die erste Stelle nicht als Variable, sondern als darzustellender Text interpretiert. Da beim zweiten $ Zeichen kein ` davor steht, wird hier wie gewohnt der Inhalt der Variablen ausgegeben. Achtung! Der gesamte Text muss dabei in doppelten Anführungszeichen stehen. Wäre ein Hochkomma um unseren Text, würde er alle darin enthaltenen Zeichen als Text anzeigen. Also sowohl die $ Zeichen, als auch den Backtick `.

Zum anderen kann er aber auch normale Buchstaben zu Sonderfunktionen umwandeln:

`t erzeugt also einen Tabstop und `n eine neue Zeile. Was es sonst noch so für Escape-Zeichen gibt erfahren Sie, wenn Sie help about_escape_characters in die PowerShell eintippen.

2.2.13.4                Regel Nr. 4

Der Backtick kann als Zeichen für einen Zeilenumbruch während der Befehlseingabe verwendet werden.

Zum Dritten können Sie damit einen Zeilenumbruch während der Befehlseingabe durchführen. Nach dem Backtick können Sie Enter drücken und kommen damit in die nächste Zeile, ohne den Befehl abzusetzen. Jetzt können Sie in der zweiten Zeile einfach weiter tippen. Wenn Sie Ihren „Roman“ dann als Befehlskette absetzen möchten, tippen Sie zum Schluss 2 x Enter. Beim ersten Mal geht er wieder in die nächste Zeile und beim zweiten schließlich wird die Befehlskette ausgeführt. Wenn Sie die Befehlseingabe abbrechen möchten, weil Sie sich vertippt haben, können Sie das mit Strg+C tun.

Get-ChildItem `
>> C
:\Windows
>>

An dieser Stelle muss ich gleich einmal mit einem Gerücht aufräumen. Viele behaupten, das geht auch mit =, |, {, [ oder (. Nun, das geht schon…aber das sind keine Zeichen um eine neue Zeile auf zu machen, sondern die PowerShell ist so clever zu merken, wenn Benutzer Fehler machen!

PowerShell Denkprozess ;-): Wenn jemand ein=tippt, dann will er sicher etwas zuordnen. Wenn der Benutzer jetzt einfach Enter drückt, dann gebe ich ihm in der zweiten Zeile die Gelegenheit es fertig zu stellen. Bevor der Typ vorm Bildschirm wieder seinen Ausraster bekommt und wild auf die Tastatur einprügelt.

Probieren Sie es einmal aus:

$a=
>> “Hutzli Butzli“
>>

Wäre das = Zeichen tatsächlich für den Zeilenumbruch, dann müsste man es in der zweiten Zeile eigentlich noch einmal eintippen. Das ist aber nicht der Fall, dann gäbe es einen Fehler. Die PowerShell ist schon recht clever und kann einige fehlerhafte Eingaben ausbaden, aber Doppelfehler sind zu viel. Der Fehler ist meist irgendwo vor dem Bildschirm. Sie schlagen doch keine armen, wehrlosen Computer? ;-)

2.2.14                     Verwenden von Rechen- und Zuweisungs-Operatoren

Operatoren kennen Sie z. B. aus der Mathematik das + Zeichen. Mit diesem Zeichen weiß jeder was mit den Zahlen links und rechts davon zu tun ist. Auch die Bedeutung eines=Zeichens kennt wohl jeder. In der PowerShell funktioniert das aber alles ein bisschen anders.

Die Grundrechenarten haben Sie bereits im Abschnitt Verarbeitung des vorangegangenen Kapitels kennen gelernt. Hier nun ein paar weitere Möglichkeiten für Berechnungen:

$a=5
$a=$a+3

Zunächst wird die Zahl 5 in die Variable $a gelegt. In der zweiten Zeile sagen wir der PowerShell erneut was in $a stehen soll und greifen dabei auf den zuvor zugewiesenen Wert (5) zurück. Im $a rechts vom=Zeichen, steht erst einmal noch der Wert 5 aus der ersten Zeile. Jetzt wird zu diesem alten Wert 3 dazu addiert und in der Variablen links vom=Zeichen abgelegt. Das auch Links $a steht ist dabei nicht weiter problematisch. Wenn Sie sich nun den Inhalt von $a anzeigen lassen, werden Sie feststellen, dass 5+3 gerechnet wurde, also 8 herauskommt.

Das Ganze kann auch, wie z.B. in der Programmiersprache C üblich, abgekürzt werden:

$a-=4

Hört sich an, als ob Joda von Krieg der Sterne an der Formulierung beteiligt war: „PowerShell Du lernen musst und die Muschel der Macht wird sein mit Dir, mein junger Padawan“. Eben hatten wir noch eine 8 in $a hinterlegt. Die Anweisung hat nun 4 von der 8 abgezogen. Jetzt sollte der Wert demnach 4 betragen, wenn Sie sich $a anzeigen lassen.

$a+=5 legt 5 drauf und $a wird 9, $a/=3 teil durch 3 und $a wird zu 3, bei $a*=2 ergibt folglich 6 in $a.

Wenn mehrere Variablen den gleichen Inhalt haben sollen, können Sie die Zuweisung auch in einem Rutsch vornehmen:

$a=$b=$c=15

Sowohl $a, $b als auch $c beinhalten nun die Zahl 15.

Wenn Sie nur 1 drauf legen möchten, oder wegnehmen können Sie ganz auf die Schnelle folgende Schreibweise verwenden:

$a--
$b++

Nach diesen beiden Zeilen enthält $a 14 und $b 16, während $c immer noch 15 ist.

Noch eine interessante Zuweisungsmöglichkeit finden Sie in dieser Form:

$a, $b, $c, $d=17, 4, 47, 11

$a enthält jetzt 17, $b 4, $c 47 und $d die 11. Natürlich können Sie noch mehr Zuweisungen machen, dazu müssen Sie einfach die Werte und Variablen weiterhin mit Komma getrennt auflisten. Die Position (nach Kommatrennung betrachtet) gibt dabei an welche Variable welchen Wert zugeordnet bekommt. $a steht ganz links, also an Position 1 vor dem=Zeichen und 17 steht ebenfalls ganz links, aber nach dem=Zeichen. $c steht somit an Position 3 und daher wird Ihr die 3. Position nach dem=Zeichen zugewiesen, also 47.

2.2.15                     Kontrollfragen

Was kommt dabei raus, wenn Sie 2+“5“ eintippen?

Was kommt dabei raus, wenn Sie „2“+5 eintippen?

Was kommt dabei heraus, wenn Sie 2+“Fünf“ eintippen?

Mit welchen Cmdlet können Sie eine Textdatei nach einem Stichwort durchsuchen?

Mit welchem Cmdlet können Sie das Datum einstellen?

Was ist der Unterschied zwischen dem Cmdlets Select-String und Select-Object?

Auf welches Cmdlet verweist der Alias Select?

Mit welchem Cmdlet können Sie einen Durchschnittswert berechnen lassen?

Wie können Sie eine bestimmte Spalte absteigend sortieren lassen?

Mit welchem Cmdlet können Sie gleiche Daten zusammenfassen lassen?

Wie können Sie eine Zahl in Hexadezimal darstellen?

Mit welchen Cmdlets können Sie eine Ausgabe in Listen- bzw. in Tabellenform erzwingen?

Wie können Sie am Bildschirm farbige Texte darstellen?

Welches Cmdlet erlaubt Ihnen in eine Datei zu schreiben?

Wie können Sie eine tabellarische Ausgabe des Bildschirms in Excel weiter verarbeiten?

Wie können Sie Ausgaben auf den Drucker schicken?

Wie können Sie Ausgaben komplett unterdrücken?

Was ist der Unterschied zwischen den Zeichen " ' `?

Wie kann man eine Zählvariable $x am einfachsten um den Wert 1 erhöhen?

Link zu den Lösungen für Ausgabebefehle

2.3     Objektorientierung

In diesem Kapitel lernen Sie die Objekt orientierte Programmierung kennen. Die PowerShell selbst arbeitet komplett Objekt orientiert, da Sie auf dem .NET-Framework aufbaut. Allerdings finden wir auch Schnittstellen zu anderen Objekt orientierten Modellen die sich fast nahtlos in die PowerShell Konsole integrieren. Auch der WMI-Zugriff und das COM-Modell werden hier erläutert.

2.3.1   Theoretische Einführung in objektorientierte Programmierung

Sie werden vielleicht Angst vor diesem Abschnitt haben, aber ich kann Ihnen versichern die PowerShell und ich werden Ihnen im Handumdrehen erklärt haben was es damit auf sich hat und dass es gar nicht so schwer ist, wie die C und Java Programmierer immer tun. Eigentlich sind Sie sogar schon mittendrin und es hat Ihnen nur noch keiner gesagt. Denn in der PowerShell gilt: Alles ist Objekt! Die Cmdlets, die Sie bis jetzt eingetippt haben sind alle Objekte und sogar die zuvor besprochenen Variablen…alles Objekte. Also eigentlich sind Sie jetzt schon ein Programmierer der Objektorientierung verwendet, Sie wussten es nur noch nicht. Aber hier werden Sie nun gleich die ganze Macht erleben, die in unserer Muschel steckt und den ersten Schlüssel zur Macht erhalten.

Am einfachsten fangen wir mit dem richtigen Leben an. Was würden Sie denn im richtigen Leben als Objekt bezeichnen? Ein Auto, einen Tisch, einen PC, einen Menschen, einen Hund usw… Da liegt es nahe zu behaupten: Alles was ich anfassen kann ist ein Objekt.

Wenn wir nun ein Objekt beschreiben wollen, wird uns auffallen, dass wir das meist mit irgendwelchen Eigenschaften tun. Die Farbe eines Autos ist z.B. Rot, Grün oder Gelb. Menschen haben eine bestimmte Augen- oder Hautfarbe und im Idealfall eine Temperatur von 37 °C. Auch die PowerShell-Objekte haben Eigenschaften. Im Englischen werden zwei Begriffe dafür verwendet. In der PowerShell werden Sie häufig die Bezeichnung Property finden. In einem Cmdlet haben wir das sogar schon eingesetzt. Auch die Spaltenüberschrift der Ausgabe ist die Bezeichnung der Eigenschaft (Property, in der Mehrzahl: Properties). Gelegentlich stoßen Sie auch auf den Begriff Attribut. Zurück zu unseren Auto Beispiel:

Ein Auto hat die Farbe Rot
entspricht in PowerShell in etwa:
Die Datei hat den Namen Explorer.exe
oder Syntaktisch:
Das Objekt hat die Eigenschaftenbezeichnung Eigenschaft.

Auch mit den Fähigkeiten wird oft ein Objekt beschrieben. Autos können fahren, Menschen können laufen, Flugzeuge und Vögel können fliegen. In der PowerShell sprechen wir aber nicht von Fähigkeiten, sondern von einer Methode. Ab und zu taucht auch der Begriff Funktion auf, der stellt in der PowerShell aber ein bisschen was anderes dar. Halten wir fest: Methoden eines Objektes können etwas machen! Eigenschaften werden immer durch Methoden verändert. Oft bekommen Sie davon aber herzlich wenig mit. Erinnern Sie sich noch, als Sie geschrieben haben $a=5? Wenn ich diesen so einfachen Vorgang Objekt orientiert beschreiben wollte, könnte ich noch ein Kapitel dran hängen ;-).

Ein Objekt besteht also grundsätzlich aus Eigenschaften und Methoden.

Sind Sie immer noch der Meinung, dass man Objekte anfassen können muss? Was ist mit der Luft? Die hat z.B. einen Geruch, oder eine Temperatur als Eigenschaft. Wenn der Wind weht, könnte man von einer Methode der Luft sprechen. Mit dieser kleinen Analogie will ich Sie nur einmal dazu anhalten, etwas über den Tellerrand hinaus zu blicken. Denn in der Programmierung und im Skripting werden Sie immer wieder feststellen, dass Ihr Standardmuster nicht passt und Sie einfach einmal nachdenken müssen, wie es sonst noch klappen könnte.

Ein Objekt kann sich aber auch aus anderen Objekten zusammensetzen. Zurück zum Auto: Ein Auto ist ein Objekt! Was ist dann ein Rad davon? Auch ein Objekt! Das Objekt Auto setzt sich also aus 4 Rädern, einer Karosserie, einem Motor usw…zusammen. Das Rad besteht wiederum aus Felge und Reifen. Der Reifen besteht aus…

Sie sehen wie vielfältig sich Objekte zusammensetzen können.

Was macht eigentlich einen Menschen aus? Da muss es doch irgendeinen Bauplan geben, der beschreibt wie ein Objekt der Art Mensch auszusehen hat. Das ist noch ein Begriff der im Zusammenhang mit Objektorientierung erklärt werden muss: die Objektklasse. Die Objektklasse sagt aus wie ein Objekt auszusehen hat. Beim Objekt Mensch z.B. wären 4 Beine doch eher unpassend für einen Hund hingegen wunderbar. Die Objektklasse Mensch würde beispielsweise festlegen, dass sich ein Mensch aus folgenden untergeordneten Objektklassen wie 2 Beinen, 2 Armen, 1 Kopf und 1 Torso, der alles miteinander verbindet, zusammensetzt. Ach ja, und eine Eigenschaft Name wäre auch nicht schlecht. Aus dieser Vorlage (der Objektklasse) kann ich nun einen bestimmten Menschen erstellen, z.B. den Martin Lehmann. O.k., davon gibt es viele, aber durch die vielen Einstellmöglichkeiten an den Eigenschaften, der Objektklasse und deren Unterklassen gibt es eine einzigartige Mischung die dann zu einem Buchautor führt. Wird ein Objekt aus einer Objektklasse erstellt nennt sich das instanziieren. Wenn Sie einmal den Begriff Instanz lesen ist damit das Objekt (z.B.: Explorer.exe) das aus einer Objektklasse (z.B.: Datei) generiert wurde gemeint. Die Objektklasse gibt weiterhin die Einstellmöglichkeiten der Eigenschaften und die Methoden vor. Die Objektklasse Kopf enthält wiederum zwei Augen. Die Augenfarbe beim Menschen kann Grau, Grün, Blau oder Braun sein und es kann zwinkern. Das ist von der Objektklasse Auge so vorgegeben. Die Eigenschaft Lila der Augenfarbe ist in der Objektklasse menschliches Auge leider nicht vorgesehen. Hey, kann da mal einer ein Update schreiben? Ich will lila - das wäre cool! Weiter hinten im Kapitel werden wir ein bisschen Genforschung betreiben, sprich in der PowerShell können wir uns einfach lila Augen basteln!

Ich fasse noch einmal kurz zusammen: Die Objektklasse gibt die Methoden und Eigenschaften von Objekten vor. Jedes Objekt ist die Instanz (Ableitung) aus einer Objektklasse. Bei der Instanziierung werden den Objekten die Eigenschaften zugewiesen und Methoden aus der Objektklasse „vererbt“.

Auf eine Objekt-Eigenschaft greifen Sie einfach in der .NET-Schreibweise zu:

Objekt.Eigenschaftenbezeichnung

oder

Objekt.Unterobjekt.Eigenschaftenbezeichnung

Denken Sie an das Beispiel Auto, könnte das so aussehen:

VW.Golf.Karosserie.Farbe

Wenn Sie die Eigenschaft Farbe ändern möchten geht das in den meisten Fällen ganz einfach:

VW.Golf.Karosserie.Farbe=“Rot“

Auf eine Methode greifen Sie in der folgenden Schreibweise zurück:

Objekt.Methode()

oder

Objekt.Methode(zu übergebender Wert)

oder

Objekt.Unterobjekt.Methode(zu, übergebende, Werte)

Aufrufen einer Methode am Beispiel Auto:

VW.Golf.MotorStarten()

Am Beispiel Mensch, um den Zeigefinger der linken Hand zu 60% zu krümmen:

MartinLehmannAusBruchkoebel.LinkerArm.Hand.Zeigefinger.Kruemmern(60)

2.3.2   Objekt-Eigenschaften in der Praxis

Wenn Sie ein Cmdlet ausführen macht das etwas für Sie. Bei den meisten Cmdlets bekommen Sie nach der Ausführung etwas auf der Standardausgabe (dem Bildschirm) angezeigt. Sie bekommen zwar Text angezeigt, aber nur weil das für uns Menschen optisch in Text besser zu verstehen ist. Das Cmdlet an sich generiert ein Objekt und durch die Standardausgabe wird es als Text auf dem Bildschirm dargestellt.

Wenn Sie Get-ChildItem ausführen, wird Ihnen das aktuelle Verzeichnis auf dem Bildschirm dargestellt. Das was Sie da sehen ist in etwa zu vergleichen, wie wenn Sie jemand auf der anderen Straßenseite laufen sehen. Stellen Sie sich eine vierspurige Straße in beide Richtungen vor. Können Sie die Augenfarbe der Person sicher erkennen? Also ich mit meinen 43 Jahren nicht mehr, obwohl ich laut Augenarzt immer noch keine Brille brauche. Wenn Sie nun die Straßenseite wechseln, können Sie sich die Person genauer anschauen. Und wenn Sie hübsch ist, Ihr eine lange Zeit in die Augen schauen. Nach einer Minute des Starrens sind Sie bestimmt in der Lage die Augenfarbe zu bestimmen. Es sei denn die Person hatte so hübsche Augen, dass Sie auch gar nicht mehr wissen wie Sie selbst heißen ;-). Genau das können Sie in der PowerShell auch machen – die Straßenseite wechseln und sich das Objekt einmal aus der Nähe betrachten. Die Cmdlets zeigen immer nur einen Bruchteil der Informationen an, die es eigentlich zu bieten hat. Der Befehl um die Straßenseite zu wechseln nennt sich Get-Member oder der kürzere Alias gm. Get-Member ist das 4. heilige Cmdlet. Wenn Sie nun die Ausgabe (egal von was, Variable, Cmdlet, Funktion, WMI oder COM) mittels einer Pipe an gm umleiten können wir uns das Objekt der Begierde ganz genau ansehen:

Get-Childitem c:\windows| Get-Member
oder kurz:
ls c:\windows | gm
Vielleicht möchten Sie auch lieber noch einmal an more pipen:
ls c:\windows | gm | more

Da sich im Windows-Verzeichnis sowohl Dateien, als auch Unterverzeichnisse befinden, sehen wir 2 Objektklassenbeschreibungen und zwar von den Objektklassen System.IO.DirectoryInfo (Verzeichnisse) und System.IO.FileInfo (Dateien). Welche Objektklassen es sind steht also immer in den Zwischenüberschriften hinter TypeName:.

Auf die Zwischenüberschrift folgt jeweils eine Tabelle mit den Methoden und Eigenschaften (Properties). Das .NET-Framework selbst kennt nur Methoden und Eigenschaften. Die PowerShell macht sich die sogenannte .NET-Erweiterbarkeit zunutze um bei den Eigenschaften noch einmal verschiedene Varianten der Eigenschaften (z.B. ScriptProperty oder NoteProperty) zu implementieren. Im Moment reicht es für Sie erst einmal aus zu wissen, dass überall wo das Wort Property drin vorkommt, es sich um eine Eigenschaft handelt. Bei Methoden ist das ähnlich, aber in diesem Beispiel haben wir nur die .NET-Methoden (ohne Erweiterung).

Beim einfachen ls bekommen Sie als Spaltenüberschrift LastWriteTime angezeigt. Schauen Sie sich die Klasse FileInfo einmal näher an. Da finden Sie in der ersten Spalte ziemlich weit unten auch die LastWriteTime. Aber das steht nicht nur die LastWriteTime, sondern auch die LastAccessTime und wenn Sie wollen auch in UTC-Zeit, statt Ortszeit. Noch einmal ein Stückchen hoch finden Sie auch eine CreationTime. Wenn Sie nun alle drei Zeiten von einer Datei wissen möchten, können Sie sich das auch anzeigen lassen. Mit select haben Sie ja schon gearbeitet. Sie erinnern sich, wie Sie damit die Eigenschaften beschnitten haben? Wo Sie abschneiden können, dürfen Sie auch drauf legen! Wenn Sie den Namen und die 3 Zeiten (ohne UTC) haben möchten geben Sie einfach ein:

ls c:\ | select Name,CreationTime,LastWriteTime,LastAccessTime

Da dieselben Eigenschaften auch bei Verzeichnissen verwendet werden, wird es für die Verzeichnisse gleichermaßen bereitgestellt. Das dies nun aus 2 unterschiedlichen Objektklassen stammt ist der PowerShell egal. Oh, wie ich diese Shell liebe (ich kenne auch JAVA und C :-P). Ja, das geht auch in den anderen Sprachen, aber was müssen Sie dafür tun (Angefangen bei Klassendeklaration…)?

Alles wo in der mittleren Spalte bei gm das Wort Property auftaucht, dürfen Sie sich gerne mit select abholen und anzeigen lassen. Da eine Methode nun einmal keine Eigenschaft ist, geht das mit den Methoden nicht.

Im Abschnitt Daten filtern im vorigen Kapitel, hatte ich Ihnen beim ps Alias geschrieben, dass ich Ihnen hier verrate wie Sie herausbekommen, wie die Eigenschaften wirklich heißen. Nun, machen Sie doch einmal ein ps | gm und schon wissen Sie die echten Namen der Eigenschaften. Hier sehen Sie jetzt z.B. auch ganz oben die AliasEigenschaft Name, die auf die tatsächliche Eigenschaft ProcessName deutet. VM ist ebenfalls eine AliasEigenschaft. Da finden Sie sicher selbst heraus, wie die eigentliche Eigenschaft heißt. Ja, auch über Prozesse lässt sich hier eine Menge mehr herausfinden, als unter DOS und das auf super einfache Art und Weise.

Sie können gm am Ende einer jeden Verarbeitungskette einsetzen, um herauszufinden welches Universum sich hinter dem Offensichtlichen verbirgt.

Select kann noch einen weiteren Trick. Anstatt sich die Objektklassen anzuschauen, können Sie sich auch alle Eigenschaften mit allen Werten liefern lassen. Das hilft besonders, wenn Sie sich unter einer Eigenschaftenbezeichnung nicht vorstellen können. Statt nun jede einzeln anzugeben können Sie einfach mit dem * Platzhalterzeichen „Alle“ angeben.

ls c:\ | select *

Ein Haufen Info, was? Sie können die Informationsflut eindämmen, indem Sie nur vom ersten, oder letzten Objekt alle Eigenschaften auslesen.

ls c:\ | select * -first 1

Aber Vorsicht! Wenn unterschiedliche Objektklassen über die Pipe kommen, wie bei einem Verzeichnisinhalt könnte im ersten Objekt als Klasse ein Verzeichnis hinterlegt sein und beim letzten Objekt eine Datei. Gut, hier sind die Unterschiede nicht so groß, aber wenn Sie mehrere gänzlich unterschiedliche Objektklassen über die Pipe bekommen, kann das ohne gm ganz schön verwirren. Wenn Sie wissen möchten welche Objektklassen generiert werden, z.B. von ls können Sie das einmal ausprobieren:

ls c:\ | gm | select typename | unique –asstring

Sie können auch direkt auf Eigenschaften in der .NET-Schreibweise zugreifen. Wenn Sie z.B. wissen möchten welcher Wochentag heute ist, führen Get-Date | gm aus. Dort sehen Sie die Eigenschaft DayOfWeek. Anstatt nun lange mit Select zu hantieren klammern Sie das Get-Date einfach ein, damit wird das Datumsobjekt generiert. Wenn Sie das so eintippen ist erst einmal kein Unterschied zwischen mit oder ohne Klammer sichtbar. Allerdings dürfen Sie, wenn Sie es klammern, einfach .Eigenschaft hinten dran schreiben. In unserem konkreten Fall also:

(Get-Date).DayOfWeek

Eine „allgegenwärtige“, aber Get-Member gegenüber unsichtbare Eigenschaft ist Count. Sollte Count einmal nicht funktionieren, probieren Sie es stattdessen einfach mit Length. Einer von beiden geht immer! Wenn Sie ein Get-ChildItem abschicken, bekommen wir mehrere Dateien und Verzeichnisse zu sehen. Aber wie viele Objekte sind das eigentlich insgesamt? Genau das verrät uns diese gut versteckte Eigenschaft.

(ls).count

Jetzt wollen Sie auch noch wissen, wie viele Dateien und wie viele Verzeichnisse? Hmmm…mal überlegen. Wenn Sie von ls | gm die Ausgabe genauer untersuchen, stellen Sie fest, dass sich die Eigenschaft PSIsContainer  von Dateien und Verzeichnissen in der Spalte Definition unterscheiden. Für Verzeichnisse steht dort True und für Dateien False. True steht für Wahr und False für Falsch. Die Eigenschaft PSIsContainer ist wahr, wenn es ein Container ist (=Verzeichnis, denn es kann ja etwas beinhalten) und falsch, wenn es keiner ist, also eine Datei. Wie wäre es mit einer Gruppierung nach dieser Eigenschaft?

ls c:\windows | group psiscontainer

Schon sehen Sie in der ersten Spalte, wie viele Verzeichnisse und wie viele Dateien im Windows-Verzeichnis liegen. In der 2. Spalte steht dann True für Verzeichnisse bzw. False für Dateien. Natürlich werden Sie noch viel elegantere Möglichkeiten kennenlernen.

2.3.3   Daten Objekt orientiert filtern

Was uns jetzt noch fehlt, ist eine SQL-ähnliche Abfrage um Daten zeilenweise zu filtern. Gegen das, was ich Ihnen nun zeige ist ein grep oder Select-String langweilig.

Wechseln Sie ins Windows-Verzeichnis, da haben Sie eine schöne Spielwiese. Wenn Sie den Inhalt anzeigen lassen, kommt da einiges auf den Bildschirm. Wenn wir nur die Dateien mit der Dateinamenerweiterung exe angezeigt haben möchten, können Sie das schon direkt über Get‑Childitem mit dem Parameter –Filter tun:

ls –filter *.exe

oder ganz frech (die PowerShell wird’s schon richten):

ls *.exe

Mit –Filter zu arbeiten ist die empfohlene Vorgehensweise, da Filterfunktionen, wenn Sie vom Cmdlet vorgesehen sind, schneller arbeiten, als die nachfolgend vorgestellte Möglichkeit. Aber zum einen hat nicht jedes Cmdlet Schalter, wie –Filter, -Include oder –Exclude und zum anderen sind diese Parameter nicht so mächtig, wie das Where-Object.

Auf das Where-Object Cmdlet gibt es gleich 2 Aliase: where und das ?. Sie dürfen natürlich einsetzten was Sie möchten, ich werde hier der Kürze und Übersichtlichkeit halber aber die kürzeste Form wählen. Die geschweifte Klammer auf, finden Sie auf der Taste 7 und die dazugehörige Klammer zu, auf der Taste 0. Um diese zu bekommen müssen Sie die Tasten zusammen mit der AltGr-Taste (rechts neben der Leertaste) drücken. Den Tiefstrich erhalten Sie mit der Großschreibtaste und der Minus-Taste links neben der rechten Großschreibtaste.

ls | ? {$_.Name –like “*.exe“}

Das macht nun genau dasselbe wie ls *.exe, nur wesentlich langsamer. Wie viel langsamer werden Sie gleich sehen, doch jetzt muss ich Sie erst einmal überzeugen, wie toll der „Umstand“ sein kann.  Das ?-Zeichen ist der Alias auf Where-Object. Der von Ihnen definierte Filter muss in geschweifte Klammern geschrieben werden.  $_ ist eine spezielle Variable(eine sogenannte automatische Variable) in der PowerShell. Sie enthält den Inhalt der Pipeline. Ja, richtig! Alle Objekte, die übergeben wurden. Da sowohl Dateien, als auch Verzeichnisse den Namen in der Eigenschaft Name speichern, können Sie durch $_.Name den Name des jeweiligen übergebenen Objekts durch den Vergleichsoperator –like mit einem Muster, wie *.exe, vergleichen. Da *.exe ein Text ist, muss dieser natürlich wieder in Anführungszeichen stehen. Heraus kommen demnach auch nur wieder Dateien (und Verzeichnisse!) deren Name auf .exe endet. Da die wenigsten Verzeichnisse mit .exe aufhören, geschweigen denn überhaupt einen Punkt enthalten, werden hier wohl auch nur Dateien erscheinen.

Was ist nun so toll an Where-Object? Nun, Sie haben doch mit Get-Member gesehen wie viele Eigenschaften so ein Objekt enthält. Mit Where-Object können Sie nicht nur nach dem Dateinamen filtern, wie es die Parameter von Get-ChildItem tun, sondern nach jeder beliebigen Eigenschaft.

Mit dem Where-Object haben Sie das 5. heilige Cmdlet kennengelernt und somit die 5 heiligen Cmdlets zusammen. Fassen wir die 5 heiligen Cmdlets noch einmal zusammen:

1.       Get-Command

2.       Get-Help

3.       Select-Object

4.       Get-Member

5.       Where-Object

Wenn Sie sich auf diese 5 Cmdlets besinnen, sind Sie in der Lage alle Aufgabenstellungen mit PowerShell zu bewältigen!

2.3.4   Messen wie viel Zeit eine Verarbeitungskette in Anspruch nimmt

So und nun die üble Nachricht, die Sache mit der Geschwindigkeit. Dazu können wir das Measure-Command einsetzen (bitte nicht mir Measure-Object verwechseln). Measure-Command misst die Verarbeitungszeit von dem was Sie hinter Measure-Object in geschweiften Klammern hineinschreiben. Die leere Hülle würde so aussehen:

Measure-Command {}

Messen wir einmal nach!

Measure-Command {ls *.exe}

Merken Sie sich bitte die Millisekunden und dann führen Sie das hier aus:

Measure-Command {ls | ? {$_.Name -like "*.exe"}}

2.3.5   Objekte miteinander vergleichen

Nun vergleichen Sie einmal die Millisekunden. Where-Object ist in diesem Fall also rund um den Faktor 5-10 langsamer! 5-10, warum so ungenau? Nun das hängt wohl damit zusammen, wie die PowerShell gerade so drauf ist und Sie ist diesbezüglich sehr launenhaft. Mit dem Cmdlet Compare‑Object können wir auch Objekte bzw. deren Eigenschaften miteinander vergleichen.  Die Syntax sieht wie folgt aus:

Compare-Object (Objekt A) (Objekt B) –Property Eigenschaft

 Tippen Sie einmal folgendes ein:

compare-object (measure-command {ls *.exe}) (measure-command {ls | `
?
{$_.name -like "*.exe"}}) –property milliseconds

Objekt A ist dabei die Variante mit dem Schalter –Filter und Objekt B die Version mit dem Where‑Object. Als zu vergleichende Eigenschaft haben Sie die Milliseconds angegeben.

Wenn Sie nun die Taste mit dem Pfeil nach oben auf Ihrer Tastatur drücken, kommt der eben eingegebene Befehl erneut. Mit Enter setzen Sie ihn erneut ab. Machen Sie das doch ein paar Mal und sehen Sie wie launisch unsere Machtmuschel ist.

Get-Process, kurz ps,  z.B. hat keinen Parameter –Filter, oder –Include. Hier kann uns also nur das Where-Object weiterhelfen.

ps | ? {$_.name –like “powershell“}

listet Ihnen alle PowerShell-Prozesse auf. Interessanter ist vielleicht eher der svchost Prozess.

Probieren Sie doch einmal, alle svchost Prozesse anzeigen zu lassen. Die Lösung finden Sie im Anhang unter Übungen.

Übungsaufgabe: svchost Prozesse auflisten

Geben Sie doch einmal folgendes ein und versuchen Sie herauszufinden, was da passiert ist:

ps | ? {$_.name –eq “powershell“} | spps

Übungsaufgabe: spps

2.3.6   Vergleichs-Operatoren und reguläre Ausdrücke

Nein, reguläre und vulgäre Ausdrücke sind nicht das Gleiche. Reguläre Ausdrücke werden in der Regel zum Filtern von Ausgaben verwendet. Bestimmt kennen Sie von anderen Shells so etwas wie dir *.exe oder ls *.sh. Reguläre Ausdrücke können so etwas auch, nur sind sie viel mächtiger. Doch zunächst zu den Vergleichs-Operatoren.

Aus anderen Programmiersprachen kennen Sie vielleicht die folgenden Anweisungen:

=, ==, <, >, >=, usw.

In der PowerShell können Sie diese nicht verwenden, doch wir haben ähnliche Möglichkeiten, die zwar mehr Tipparbeit erfordern, aber auch vielfältiger sind.

2.3.6.1   Vergleichs-Operatoren für Zahlen

Wenn Sie wissen möchten, ob etwas größer ist als etwas anders können Sie auf –gt (greater than=größer als) oder –lt (less than=kleiner als) zurückgreifen.

Nur Zeile 2 und Zeile 6 ergeben True, weil alle anderen Aussagen nun einmal falsch sind. Wenn Sie größer gleich oder kleiner gleich auswerten möchten müssen Sie auf –ge (greater or equal) bzw. ‑le (less or equal) zurückgreifen.

Hier ist nur Zeile 3 und Zeile 5 False, da in Zeile 3: 1 nicht größer, aber auch nicht gleich 2 ist und in Zeile 5: 2 nicht weniger und auch nicht gleich 1 ist.

Wenn Sie wissen möchten über etwas ungleich ist können Sie das mit –ne (not equal=nicht gleich) abfragen.

Hier bekommen wir in Zeile 2 False, weil 1 und 1 gleich sind. Wir haben gefragt, ob die Linke und Rechte Seite vom Vergleichsoperator unterschiedlich sind. Nein, das sind sie nicht, also: False.

Dem = oder == zum exakten Vergleichen kommt der Operator –eq wie equals gleich. Sie können Operatoren auch direkt ausprobieren z.B.:

Die erste Zeile liefert True, weil 1 nun einmal genauso viel wie 1 ist und die zweite Zeile sagt False, weil 1 eben nicht 2 ist. –eq eignet sich weniger zum Vergleichen von Texten, da eine genaue Übereinstimmung gegeben sein muss. Denn:

1 –eq 1.0001

ist nun einmal nicht exakt gleich, sondern nur annähernd, also kommt hier auch False raus. D.h. wenn wir Texte vergleichen müssen die ebenfalls identisch sein:

…und nicht nur so ähnlich. Zumindest bei –eq.

2.3.6.2   Vergleichs-Operatoren für Text

Wenn wir Texte mit Platzhalterzeichen wie ? oder * vergleichen möchten müssen wir zu –like greifen, so wie im vorangegangenen Abschnitt mit dem Where-Object. Damit wissen Sie auch gleich wo Sie u. a. die Vergleichs-Operatoren einsetzen können.

Nur bei der 3. Zeile kommt False raus, weil ABC nicht mit B beginnt. Bei den Zahlenvergleichen gab es –eq und –ne. Das haben wir auch bei –like. Das Gegenteil dazu ist: –notlike.

Alle Vergleichs-Operatoren können also noch angepasst werden. Ihnen stehen für die Anpassung not, c und i zur Verfügung. Diese können Sie zwischen das und den eigentlichen Vergleich setzen, wie eben beim Beispiel –like und -notlike. not dreht den Vergleich logisch herum. c macht das Ganze Case-Sensitiv, sprich es wird beim Vergleich auf Groß-/Kleinschreibung geachtet. i ist sinnlos, weil es das Gegenteil von c ist, sprich Ignore-Case-Sensitivity. Da standardmäßig sowieso nicht nach Groß-/Kleinschreibung geschaut wird, werden Sie wohl nie ein i extra reinschreiben.

ls c:\windows | ? {$_.name –clike „W*“}

Gibt nur die Dateien aus dem Windows-Verzeichnis aus, die mit einem großen W beginnen, aber nicht mehr diejenigen, welche mit einem kleinen w beginnen. Die folgende Abbildung zeigt zuerst den Vergleichsoperator –like und danach den Operator –clike im Einsatz:

2.3.6.3   Reguläre Ausdrücke verwenden um mächtige Textfilter zu erstellen

Die mächtigste Waffe beim Textvergleich sind wie gesagt die regulären Ausdrücke.  Für diesen Vergleichsoperator verwenden Sie –match, wenn Sie alle Übereinstimmungen haben möchten und –notmatch, für alles, was nicht passt. Das ^ Zeichen bekommen Sie mit der Taste links neben der Taste für die Zahl 1 (im Alphanumerischen Bereich der Tastatur, nicht im Zahlenblock). Das ist allerdings genauso seltsam, wie beim Backtick. Erst müssen Sie die ^ Taste drücken und dann noch eine weitere (egal welche) hinterher, damit das ^ Zeichen erscheint.

ls c:\windows | ? {$_.Name –match „^w.*\.exe$”}

Den grundsätzlichen Aufbau des Where-Objects habe ich im vorangegangenen Abschnitt erläutert, daher will ich hier nur auf den Bereich zwischen den Anführungszeichen eingehen. Das ^ Zeichen sagt aus, dass das Zeichen, welches danach kommt am Anfang stehen muss. Der Dateiname muss also mit einem w beginnen, weil das w auf das ^ Zeichen folgt. Das $ Zeichen am Schluss sagt genau das Gegenteil aus, sprich: Die Zeichen die vor dem $ Zeichen sind müssen am Ende stehen. Daher muss der Dateiname auf .exe enden. Der Punkt entspricht dem Fragezeichen unter DOS. Er ist also ein Platzhalter für irdendein einzelnes Zeichen (Symbol, Buchstabe, Zahl…ganz egal). Das auf den Punkt folgende Sternchen ist ein sogenannter Wiederholungsoperator und sagt aus, dass das Zeichen, welches vorne dran steht, 0 mal, 1 mal oder mehrfach vorkommen kann. Der Witz an der Sache ist der, dass der Punkt ja selbst ein Platzhalter für ein Zeichen ist. Durch die Kombination .* haben wir das, was wir von DOS her als den * Platzhalter kennen. Was soll nun das \ Zeichen? Nun gerade haben Sie gelesen, dass der Punkt ein Platzhalterzeichen ist und somit eine besondere Funktion innehat. Das \ Zeichen sorgt dafür, das der Punkt seiner Sonderfunktion beraubt wird und nunmehr nur noch als ein ganz normales Zeichen, sprich ein . interpretiert wird. Ohne das \ Zeichen könnte die Datei mit „2exe“, „aexe“, !exe“ oder sonst wie, Hauptsache mit 4 Zeichen wovon die letzten 3 „exe“ sind, enden.

Die eckigen Klammern für das nächste Beispiel bekommen Sie mit AltGR+8, bzw. AltGR+9.

ls c:\windows\system32 | ? {$_.Name –match „^[wrt].*\.(exe|com)$”}

Das Beispiel ähnelt dem vorigen. Allerdings haben wir das w mit einem r und einem t in eckigen Klammern und es geht ins System32-Verzeichnis, da im Windows-Verzeichnis keine *.com-Dateien liegen. Die eckigen Klammern bedeuten, dass alle eingeklammerten Zeichen an dieser Position stehen können, aber kein anderes. Dadurch, dass das ^ Zeichen vor der eckigen Klammer steht, bedeutet es, dass der Dateiname mit w, r oder t beginnen muss. Die runde Klammer mit der Pipe funktioniert ähnlich. Aber hier bezieht es sich nicht auf ein einzelnes Zeichen, sondern auf eine Zeichenkette. Das Pipe-Symbol dient hier als „Oder“. Da nach der runden Klammer ein $ Zeichen steht, muss die Datei also auf exe oder com enden. Im ersten Moment mag man den Eindruck haben, dass keine *.com-Dateien dabei sind, aber schauen Sie einmal genau hin. Eine win.com sollte da auch mit dabei sein. Damit haben Sie bereits die wichtigsten Sonderzeichen der regulären Ausdrücke kennengelernt, mit denen man schon verdammt viel anstellen kann.

Zeit für eine knifflige Übungsaufgabe! Listen Sie alle Dateien aus dem System32-Verzeichnis auf, die an der 2. Stelle ein w oder ein t stehen haben und die Dateiendung exe oder com.

Übungsaufgabe: RegEx

Für die Freaks sei die Lektüre help about_Regular_Expressions und help about_Comparison_Operators empfohlen, wo Sie eine sehr gute Erklärung für noch weitere reguläre Ausdrücke und Vergleichs-Operatoren finden.

2.3.7   Logische Verkettung von Vergleichs-Operatoren mit Logikoperatoren

Um z.B. die Where-Object Abfragen noch detaillierter zu gestalten haben Sie die Möglichkeit sogenannte Logische Operatoren einzusetzen. Damit erhalten Sie die Möglichkeit die einzelnen Vergleiche mit „und“, „oder“ bzw. „ist nicht“ zu verknüpfen.

2.3.7.1   Die Not Verknüpfung und boolsche Variablen

$true und $false sind fest definierte Variablen der PowerShell (so wie $_), die den Wert Wahr bzw. Falsch enthalten. Diese sollen hier nur verdeutlichen, wie man einen Wahrheitswert außer mit –not noch negieren (logisch umdrehen) kann. Mit den nachfolgenden Zeilen wird dargestellt, wie man anhand der Eigenschaft PSIsContainer, auf eine weitere Weise Dateien von Verzeichnissen unterscheiden kann.

Zeigt Ihnen alle Verzeichnisse (psiscontainer=True, ja es ist ein Behälter) auf Laufwerk c:.

Zeigt Ihnen alle Dateien (psiscontainer=False, nein es ist kein Behälter) im aktuellen Verzeichnis. Viele Wege führen nach Rom! Sicher fallen Ihnen noch ein paar mehr Varianten ein, die auch zum Ziel führen.

2.3.7.2   Die And Verknüpfung (und)

Wenn Sie alle Dateien aus dem Windows-Verzeichnis sehen möchten, die min. 50000 Byte groß sind und die Dateinamenerweiterung exe haben geben Sie folgendes ein:

$_.length fragt die Dateigröße in Byte ab. Demzufolge müssen wir vergleichen, ob diese größer als 50000 Byte sind. Achtung! Hier geht es um eine Zahl, also keine Anführungszeichen. Dann kommt die logische UND-Verknüpfung mit –and. Daraufhin folgt die bereits bekannte Abfrage der Namenseigenschaft die geprüft wird, ob sie auf .exe endet.

2.3.7.3   Die Or Verknüpfung (oder)

Nun lassen Sie sich doch einmal alle Dateien anzeigen, die größer als 50 KB sind oder die Dateinnamenerweiterung exe haben.

Das ist also dasselbe Spiel wie eben bei –and, welches einfach durch –or ersetzt wird.

Alles recht logisch, oder? Dann testen Sie doch einmal Ihr logisches Denken und Ihr Erinnerungsvermögen. Listen Sie doch einmal alle Verzeichnisse und log-Dateien aus dem Windows-Verzeichniss, deren letzter Schreibzugriff nach dem 20.11.2010 liegt.

Übungsaufgabe: Where-Object

Weitere Informationen zu logischen Operatoren finden Sie in:
help about_Logical_Operators.

2.3.8   Objektmethoden in der Praxis

Beim Aufruf von Get-Member sind Ihnen sicher bereits die Methoden (in der Spalte mit der Überschrift Membertype)  aufgefallen. Nun wollen wir die Objekte einmal etwas machen lassen.

2.3.8.1   Dateien ver- und enschlüsseln

Bei den Dateien gibt es u. a. die Methode Encrypt und Decrypt. Diese beiden Methoden dienen zum ver- und entschlüsseln von Dateien mittels EFS. Erstellen Sie sich ein Verzeichnis mit dem Namen Test auf Laufwerk c:, falls noch nicht geschehen. Wechseln Sie auf der PowerShell in das Verzeichnis und erstellen Sie mit set-content c:\test\opfer.txt “Opfer“ eine Datei. Prüfen Sie ob bei

ls | ? {$_.name –eq „opfer.txt“}

wirklich nur diese eine Datei dabei herauskommt. Durch

ls | ? {$_.name –eq „opfer.txt“} | gm

stellen Sie fest, dass es hier eine Methode Decrypt und eine Methode Encrypt gibt. Sie haben doch Ihr Laufwerk C: mit NTFS formatiert ;-)? Bei FAT würde das natürlich nicht klappen, da EFS-Verschlüsselung ein Feature von NTFS ist. Aber heutzutage hat wohl niemand mehr ein Laufwerk C:, das mit FAT formatiert ist. Sie erinnern sich noch an die Information aus der Theorie, dass man Methoden immer mit einer runden Klammer am Schluss aufruft? Um die Methode Encrypt anwenden zu können gibt es zwei Möglichkeiten. Bei der ersten arbeiten wir mit einer Variablen:

$Datei=ls | ? {$_.name –eq „opfer.txt“}

Jetzt ist unsere Datei in der Variablen hinterlegt. Das können Sie prüfen, indem Sie einfach einmal $Datei eintippen und bestätigen. Da kommt genau dasselbe heraus, wie mit der kompletten Verarbeitungskette. Geben Sie nun einmal

$Datei | select Attributes

ein. Damit sehen Sie, dass Sie nichts sehen ;-). Zumindest steht hier kein Attribut Encrypted. Im Explorer erkennen Sie verschlüsselte Dateien in der Regel daran, dass diese grün statt schwarz angezeigt werden, falls Sie dort schauen möchten. Dazu  muss allerdings im Explorer unter Extras/Ordneroptionen/Ansicht ein Häkchen bei Verschlüsselte oder komprimierte NTFS-Dateien in anderer Farbe anzeigen gesetzt sein. Auf modernen Systemen ist das standardmäßig gesetzt. Um nun die Methode anzuwenden geben Sie einfach

$Datei.encrypt()

ein. Nun noch ein

ls | select Name,Attributes

hinterher und Sie sehen, dass Ihre Datei nun verschlüsselt ist, da Sie das Attribut verschlüsselt trägt. Gerne dürfen Sie das auch im Explorer prüfen.

Zum Entschlüsseln werden wir den direkten Zugriff wählen, ohne den Umweg über die Variable. Dazu müssen Sie die Verarbeitungskette in runde Klammern setzen, damit das Objekt generiert wird und danach können Sie ähnlich wie bei der Variante mit der Variablen die Methode hinten dran schreiben:

(ls | ? {$_.name –eq "opfer.txt"}).decrypt()

Ob das funktioniert hat können Sie wieder mit ls | select Name,Attributes prüfen. Die Schlaumeier unter meinen Lesern werden sich bestimmt denken, dass das auch mit
$Datei | select Name,Attributes
funktionieren müsste.  U. U. werden Sie aber unterschiedliche Ergebnisse zwischen direktem und dem Zugriff über eine Variable feststellen. Sie haben sich das Dateiobjekt zu Beginn in $Datei geholt und dann den Code, der durch die Methode Encrypt aufgerufen wird ausgeführt. Damit wurde die Datei verschlüsselt. Doch wer hat die Objekt-Eigenschaften, aktualisiert? Richtig, niemand! D. h. in $Datei.Attributes steht immer noch, dass die Datei entschlüsselt vorliegt, obwohl bei einer direkten Abfrage auf die Datei nun steht, dass sie verschlüsselt ist. Das bekommen Sie aber schnell wieder synchron in dem Sie einfach die Methode $Datei.refresh() aufrufen. Dadurch werden in diesem Fall die Objekt-Eigenschaften aktualisiert. Nicht an jedem Objekt gibt es die Methode Refresh. Zum einen weil das an manchen Objekten gar nicht nötig ist und zum anderen weil ein anderer Methodenprogrammierer u. U. die Bezeichnung Update sinnvoller findet. Ein bisschen muss man schon mitdenken, aber man findet eigentlich immer recht schnell nach was man sucht.

Eine leichte Aufgabe: Löschen Sie die Datei Opfer.txt mithilfe einer Methode, nicht über das Cmdlet Remove-Item oder deren Aliase!

Übungsaufgabe: Datei löschen Methode

2.3.8.2   Objekt-Typen identifizieren

In der PowerShell gilt der Spruch: Alles ist ein Objekt! Sie erinnern sich vielleicht noch an die Möglichkeit die Versionsnummer der PowerShell abzufragen. Da gab es 2 Möglichkeiten: Die Variable $host und das Cmdlet Get-Host. Schauen wir uns die doch einmal genauer an. Mit Get-Member werden Sie herausfinden, dass die gelieferten Ansichten für beide Kommandos identisch sind. In beiden finden Sie daher die Methode GetType(). Diese Methode werden Sie nicht nur hier finden, sondern GetType ist an jedem .NET-Objekt vorhanden und kann daher auf jedes Objekt angewendet werden. Vergleichen Sie doch einmal die Ausgaben der folgenden beiden Befehle:

$host.gettype()

und

(Get-Host).GetType()

Na?! Identisch! Beide liefern also das Objekt InternalHost in der Spalte Name, welches auf die Objektklasse oder den sogenannten BaseType System.Management.Automation.Host.PSHost zurückzuführen ist.

Von nun an bleibe ich bei $host der kürzeren Schreibweise wegen, aber was nun kommt gilt natürlich genauso für das Cmdlet Get-Host.

Beim Get-Member auf $host haben Sie in der letzten Zeile sicher die Eigenschaft Version bemerkt. In der 3. Spalte mit der Überschrift Definition finden Sie Angaben auf welche Objektklassen die Methoden und Eigenschaften zurückzuführen sind. Bei der Version steht hier System.Version. Wenn Sie $host.Version aufrufen, bekommen Sie wiederum 4 Spalten. Das bedeutet also, das die Eigenschaft Version eigentlich gar keine Eigenschaft ist, sondern selbst auch wieder ein Objekt! Rufen Sie nun $host.Version | gm auf, sehen Sie das z.B. Major wiederum als Eigenschaft, basierend auf der Objektklasse System.Int32, gelistet ist. Int32=32 Bit Integer = eine Zahl zwischen -2147483648 und +2147483648. Es kann also noch ein paar weitere Versionen der PowerShell geben. Ach ja und die –Zahlen waren die Beta-Phase ;-).

2.3.8.2.1    Datentypen für ganze Zahlen

Zunächst einmal eine kurze Übersicht zu den Wertebereichen der Zahlen. Die kleinste Speichereinheit ist das Byte und kostet am wenigsten Speicher. Sie können Variablen Speichertypen zuweisen, indem Sie in eckigen Klammern die Bezeichnung voranstellen.

[Byte] $a=5

Wenn Sie wissen möchten, ob es funktioniert hat, nutzen Sie wieder die Methode GetType.

$a.gettype()

In der Spalte Name taucht nun Byte auf. 1 Byte=8 Bit, also binär 8 x 0 oder 1. Mit 8 x 0 oder 1 gibt es 256 Kombinationsmöglichkeiten. Das bedeutet, dass die höchste mögliche Zahl dezimal 255 ist, denn wir haben ja auch noch eine 0. Probieren Sie folgendes:

$a=0

$a=255

$a=256

$a=-1

Ergebnis:

Bei den letzten beiden gibt es Gemecker von der PowerShell, weil Sie der Variablen $a explizit den Datentyp Byte zugewiesen haben. Versuchen Sie dann außerhalb des Speicherbereiches zuzugreifen, bekommen Sie eine auf den Deckel ;-).

Bislang haben wir einfach nur Variablen Zahlen oder Texte zugewiesen, ohne uns über den Datentyp Gedanken zu machen. Dank PowerShell müssen wir das auch nicht. PowerShell nimmt bei Zahlen automatisch Int32. Das mag Ihnen verschwenderisch vorkommen, denn wie Sie wahrscheinlich schon vermuten, werden hier 32 Bit für die Zahl benötigt, auch wenn Sie lediglich eine 5 hineinlegen. 32 Bit=4 Byte=4 x 8 Bit. Also vier Mal so viel wie für ein Byte. Bei heutiger Speicherausstattung von PCs ist das aber zu verschmerzen. Schneller oder langsamer wird Ihr PC dadurch auch nicht. Bei Zahlen außerhalb des 32-Bit Integerbereiches (-2147483648 bis +2147483648), also beispielsweise bei einer Zuweisung: $a=3000000000, gibt es keinen Fehler, sondern PowerShell nimmt automatisch den nächstgrößeren Datentyp Int64. Der reicht dann von -9223372036854775808 bis +9223372036854775808 und sollte wohl für einfache administrative Tätigkeiten ausreichen. Sie wollen hier doch hoffentlich keine Spaceshuttleflugbahnen berechnen? Da würde ich Ihnen dann für Quick & Dirty zu Java raten, das ist leichter als in C und nicht wirklich langsamer. Die PowerShell ist für solche Berechnungen sowieso zu langsam.

Ärger bekommen Sie nur, wenn Sie explizit eine Variable als Int32 definiert haben und Ihr einen größeren Wert zuweisen möchten. Ansonsten konvertiert PowerShell automatisch das Int32-Objekt in ein Int64. Der Beweis:

Bis hier hin, alles wunderbar, aber jetzt:

Den letzten gettype können Sie sich sparen, denn es gab ja bei der Zuweisung von 2147483648 bereits Ärger.

2.3.8.2.2    Datentypen für Kommazahlen

Die bisher vorgestellten Zahlen unterstützen nur ganze Zahlen. Für Kommazahlen haben wir die Datentypen Float oder Single und Double. Standardmäßig wird Double verwendet, da der Wertebereich von -1,79769313486232E+308 bis +1,79769313486232E+308 wohl für die meisten Aufgaben ausreicht. Float und Single sind identisch und für Speichergeizkragen geeignet, da der Wertebereich hier „nur“ -3,402823E+38 bis +3,402823E+38 reicht und dementsprechend weniger Speicher benötigt wird.

2.3.8.2.3    Datentypen für Wahrheitswerte

Wenn Sie ein $true.gettype() ausführen sind Sie eigentlich schon im Bilde. Der Typ ist Boolean und kann natürlich ebenso von Ihnen selbst bei der Variablenerstellung definiert werden. Die Variable kann dann wie ein An- u. Ausschalter verwendet werden. Entweder enthält Sie $true, oder $false. Sie können auch Zahlen benutzen: 0=false und 1=true

2.3.8.2.4    Datentypen für Zeiten

Auch die Zeit hat einen eigenen Datentyp:

$zeit=get-date

$zeit.gettype()

Liefert in der Spalte Name DateTime zurück. Dies ist immer ein kombinierter Wert aus Datum und Uhrzeit, da die Zeit absolut geführt wird.

2.3.8.2.5    Datentypen für Texte

Es gibt nur einen Datentyp für Texte und das ist String.

$eingabe=read-host „Tippen Sie einen Text

$eingabe.gettype()

Damit kommen wir gleich zu einer wichtigen Erkenntnis! Sie haben hier den Datentype String nicht explizit zugewiesen. Das bedeutet, Sie können nun ohne Bauchschmerz einfach behaupten:

$eingabe=5

$eingabe.gettype()

Sehen Sie, alles gut! $eingabe wurde automatisch zum Datentyp Int32 konvertiert. Jetzt ist $eingabe also eine Zahl. Doch passen Sie einmal auf und geben Sie bitte eine Zahl ein:

$eingabe=read-host "Tippen Sie eine Zahl"

$eingabe.gettype()

Sehen Sie genau das ist der Grund, warum es manchmal sinnvoll ist einen Datentyp explizit zuzuweisen und warum Sie sich mit der Möglichkeit auskennen sollten.

[int32] $eingabe=read-host "Tippen Sie eine Zahl"

$eingabe.gettype()

Denn es können auch andere interessante Dinge passieren, wenn man über Datentypen nicht Bescheid weiß. Starten Sie für das nachfolgende Beispiel am besten eine frische Shell, sonst tauchen noch andere Merkwürdigkeiten auf:

$a=3

$a.gettype()

$b=“2“

$b.gettype()

$c=$a+$b

$c

$c.gettype()

$c=$b+$a

$c

$c.gettype()

Bis hierhin ist das Beispiel noch selbsterklärend, aber jetzt wird es ominös:

$d=”Acht”

$d+$a

$a+$d

Sehr faszinierend! Bei $d+$a hat die PowerShell festgestellt, dass der erste Operand $d ein String ist und hat kurzerhand $a ebenfalls in einen String konvertiert und einfach verkettet. Bei $a+$d ist der erste Operand $a aber eine Zahl. Also versucht PowerShell den zweiten Operand passend zu machen indem sie versucht den String $d in eine Zahl zu konvertieren. PowerShell ist schlau, aber nicht so schlau, dass er aus einem Text „Acht“ auch eine Zahl 8 machen kann. Das Thema hatten Sie schon, nur hier haben Sie es genauer unter die Lupe genommen.

Im Anhang finden Sie eine Übersicht aller Datentypen.

2.3.8.3   Textverarbeitung mittels Objektmethoden

Vielleicht haben Sie auch schon einmal einen Get-Member auf eine Variable des Typs String gemacht. Bestimmt sind Ihnen dabei die vielen Methoden aufgefallen. Diesen sehr wertvollen Methoden zur Textverarbeitung wollen wir hier nun auf den Grund gehen. Fangen wir ganz gemütlich an:

$autor="Martin Lehmann"

Erstellt einen einfachen String, wie Sie das bestimmt schon mehrfach getan haben.

Mittels der Methode CompareTo können Sie prüfen ob ein bestimmter Text in Ihrer Variablen vorkommt.

$autor.compareto("Martin Lehmann")

Liefert eine 0 bei exakter Übereinstimmung.

$autor.compareto("Martin")

Liefert eine 1 bei teilweiser Übereinstimmung.

$autor.compareto("Xaver")

Liefert eine -1 wenn es gar nicht passt.

Anstatt dem Text mit den Anführungszeichen können Sie natürlich auch mit dem Inhalt anderer Variablen vergleichen. Beispielsweise so:

$IhrName=“Ihr Name

$autor.compareto($IhrName)

Wenn hier eine 0 rauskommt, schreiben Sie mir doch einmal eine E-Mail und sagen „Hallo, ich habe Ihr Buch gelesen und könnte somit auch als der Autor durchgehen.“. ;-)

Wenn Sie einen Wahrheitswert bei einem Vergleich zurück haben möchten können Sie auch auf die Methode Contains zurückgreifen:

$autor.contains("tin")

Liefert Ihnen $true als Rückgabewert.

$autor.contains("ni")

Liefert Ihnen $false als Rückgabewert.

Wenn Sie wissen möchten ob der zu vergleichende Text an Anfang, oder am Ende vorkommt, können Sie die Methoden StartsWith und Endswith einsetzen:

$autor.startswith("Lehmann")

Liefert $false als Wahrheitswert.

$autor.startswith("Martin")

Liefert $true als Wahrheitswert.

$autor.endswith("Lehmann")

Liefert $true als Wahrheitswert.

$autor.endswith("Martin")

Liefert $false als Wahrheitswert.

Die Position eines Textabschnittes innerhalb eines Strings ermitteln können Sie mit IndexOf:

$autor.indexof("Lehmann")

Nur wenn der komplette Vergleichstext gefunden wird, gibt es die Position vom ersten übereinstimmenden Zeichen, also das L von Lehmann zurück. Da PowerShell in diesem Fall (und auch in den meisten anderen) bei 0 zu zählen beginnt, wird eine 7 als Ergebnis zurück geliefert.

$autor.indexof("a")

Liefert eine 1 zurück. Das a von Martin, aber nicht mehr das in Lehmann.

Wenn Sie lieber von hinten nach einem Zeichen oder Text suchen möchten benutzen Sie LastIndexOf:

$autor.lastindexof("Lehmann")

Liefert wieder die 7 (das wird also nach wie vor von vorne gezählt). Die Position des L von Lehmann wird zurückgegeben, durchläuft aber bei der Suche den String von rechts nach links.

$autor.lastindexof("a")

Liefert entsprechend die 11. Das a von Lehmann, aber nicht mehr das in Martin, weil bei LastIndexOf von hinten gesucht wird.

Wenn Sie einzelne Zeichen suchen, die Reihenfolge gesuchter Zeichen aber keine Rolle spielt, verwenden Sie die Methode IndexOfAny.

$autor.indexofany("Lr")

Liefert eine 2 zurück, da das r von Martin noch vor dem L von Lehmann gefunden wird.

Texte zusammenkleben geht einfach über das + Zeichen. Vielleicht möchten Sie aber auch einen Text an einer bestimmten Position in einen anderen einfügen. Das klappt prima mit der Methode Insert:

$autor.insert(0,"Autor ")

Fügt das Wort Autor am Anfang („nulltes“ Zeichen) ein. Gut, dass hätte man auch mit "Autor "+$autor regeln können. Aber das hier ist ein klassischer Fall für Insert:

$autor.insert(6,"des Buches ist ")

Damit haben wir den Text mitten rein gepflanzt. Allerdings beachten Sie bitte, dass die Standardausgabe der Bildschirm ist! Wir haben mit der Methode Insert nicht den Inhalt der Variablen verändert. Wenn Sie das möchten, müssten Sie schreiben (heben Sie sich das Testen aber bitte für den Schluss des Abschnitts auf, damit wir uns hier die Variable nicht „verbiegen“):

$autor=$autor.insert(6,"des Buches ist ")

Wenn Sie die Zeichen einmal untereinander, statt nebeneinander haben möchten verwenden Sie die Methode GetEnumerator:

$autor.getenumerator()

Mit Leerzeichen auffüllen können Sie mit den Methoden PadLeft und PadRight:

$autor.padleft(30)

Reserviert 30 Zeichen in die der Inhalt rechtsbündig eingefügt wird. Hier wird also auf der linken Seite mit Leerzeichen aufgefüllt, daher der Name.

$autor.padright(30)+“Endtest“

Reserviert 30 Zeichen in die der Inhalt linksbündig eingefügt wird. Damit man das auch mitbekommt, habe ich noch das +“Endtest“ angehängt.

Das Gegenteil von PadLeft und PadRight finden Sie in den Trim Methoden:

$autor.trim()

Entfernt alle links und rechts stehenden Leerzeichen. In diesem Beispiel passiert daher nichts, das Leerzeichen in der Mitte bleibt erhalten.

($autor.padleft(30)).trimstart()

Entfernt alle Leerzeichen auf der linken Seite. Erst haben Sie mit PadLeft Leerzeichen hinzugefügt, diese dann aber wieder mit TrimStart entfernt.

$autor.trimend()

Würde alle Leerzeichen auf der rechten Seite entfernen, wenn da welche stehen würden. ;-)

Wo man etwas reinsteckt, kann man auch etwas rausholen. Hierfür können Sie auf die Methode Remove zurückgreifen:

$autor.remove(6,1)

Entfernt 1 Zeichen an der 6. Position. Also in dem Fall das Leerzeichen.

Das Gegenteil davon finden Sie in der Methode SubString. Remove lieferte Ihnen das zurück was übrig bleibt, während SubString das Ausgeschnittene zurück liefert:

$autor.substring(7,3)

Schneidet ab Position 7, 3 Zeichen aus und gibt diese zurück. In diesem Falle also Leh.

Wenn Sie nur ein einzelnes Zeichen extrahieren möchten greifen Sie auf die Methode Char zurück:

$autor.chars(5)

Liefert das Zeichen der 5. Position, also das n von Martin.

Texte ersetzen können Sie mit der Methode Replace:

$autor.replace("Lehmann","Nachname")

Tauscht das Wort Lehmann gegen das Wort Nachname.

Um Texte durch bestimmte Zeichen zu trennen, wie z.B. einer Tabelle können Sie auf die Methode Split zurückgreifen:

$autor.split(" ")

Trennt den Inhalt an den Positionen von Leerzeichen.

Die einfachen Dinge des Lebens:

$autor.tolower()

Macht aus allen enthaltenen Großbuchstaben Kleinbuchstaben.

$autor.toupper()

Macht aus allen enthaltenen Kleinbuchstaben Großbuchstaben.

Hier noch ein Screenshot der zu erwartenden Ergebnisse:

Jetzt haben Sie alle Methoden der Stringverarbeitung kennengelernt.

2.3.8.4   Besondere Variablentypen: Array

Ein gutes Verständnis von Variablen haben Sie nun bereits. Jetzt wollen wir das noch etwas vertiefen mit mehrdimensionalen Variablen. Also Variablen, die nicht nur einen einzelnen Wert sondern mehrere enthalten können.

Auch mehrdimensionale Variablen, sogenannte Arrays können Sie explizit zuweisen mit dem Begriff [Array]. Auch hier ist das aber nicht unbedingt notwendig, da die PowerShell in der Regel erkennt was tun ist. Um mehrere Werte zuzuweisen gehen Sie wie folgt vor:

$array=“Eins“,“Zwei“,“Drei“

Der Zugriff und die damit verbundene Ausgabe auf dem Bildschirm kann so aussehen:

$array

In einem Array wird automatisch eine Indexnummer geführt. Der erste Wert erhält die Indexnummer 0, der zweite die 1 usw... Um auf einen bestimmten Wert aus dem Array zuzugreifen, können Sie diese Indexnummer einfach in eckigen Klammern hinten dran schreiben:

$array[0]

Dies liefert den Text Eins, oder

$array[2]

liefert natürlich den Text Drei zurück.

Wenn Sie einen leeren Array erstellen möchten geht dies auch mit:

$leer=@()

Um zu überprüfen, ob es wirklich ein Array ist, können wir auch hier wieder die GetType Methode anwenden:

$leer.gettype()

Spannend daran ist, dann es eigentlich gar keinen Array gibt. Zumindest steht nicht wie bei den anderen Datentypen, der Type in der Spalte Name, sondern nur der Verweis auf die Objektklasse unter BaseType bringt uns schließlich zu dem Schluss, dass es ein Array sein muss. Der Objekt-Typ ist hier tatsächlich Objekt. Das rührt daher, dass ein Array unterschiedliche Objekt-Typen in sich aufnehmen kann. Wir können also kreuz und quer, Texte, Zeiten, Zahlen und andere Objekte einfach in einem Array zusammen mixen. Glauben Sie nicht? Dann machen Sie doch einmal das hier:

$array+=2

$array+=get-date

$array

$array[4]

Damit haben Sie auch gleich die Möglichkeit gesehen einen Array zu erweitern. Aber woher wissen Sie nun, dass wir hier wirklich eine Zahl bzw. eine Zeit hinzugefügt haben? GetType weiß Rat:

$array[3].gettype()

$array[4].gettype()

$array[2] hatten wir mit dem Text Drei gefüttert. Durch $array+=2 haben wir $array um ein $array[3] vom Datentyp Int32 erweitert und die Zahl 2 hineingeschrieben. Daher erhalten wir mit $array[3].gettype den Datentyp dieses einen Eintrags. Ähnlich verhält es sich mit der Zeitangabe. Da jetzt $array[3] bereits existiert (da steht die Zahl 2 drin), wird ein neuer Eintrag unter $array[4] angelegt. Weil er eine Zeit aufnehmen muss wird natürlich automatisch der Datentyp DateTime verwendet.

Das gemeine am Array ist, dass wir hier kein Get-Member sinnvoll nutzen können. Aber natürlich können wir das einmal ausprobieren:

$array | gm

Statt dem Array selbst, hat Get-Member die „Mitglieder“ ausgewertet. Vielleicht erinnert Sie das jetzt an einen ls | gm auf ein Verzeichnis, dass sowohl Dateien, als auch Unterordner enthält. Genau! Auch hier bekommen wir einen Array mit allen Verzeichnissen und Dateien geliefert. Das bedeutet Sie können die Technik mit dem Index, auch auf das anwenden was über die Pipe kommt, oder auch direkt:

(ls c:\Windows)[3]

Gibt das 4. Element (die PowerShell fängt bei 0 das Zählen an!) aus dem Windows-Verzeichnis zurück.

(ls c:\Windows)[-3]

Macht das Ganze von unten. Aaaaber! Gemerkt? Was habe ich eben noch so großspurig von mir gegeben? Die PowerShell fängt bei 0 an zu zählen. Ätsch! Nicht, wenn wir das Pferd von hinten aufsatteln, da fängt Sie bei -1 an, dann -2 usw… Da haben wir eine der Ausnahmen die wieder einmal die Regel bestätigt.

Was in die eine Richtung klappt geht natürlich auch in die andere:

$array | select –last 2

Gibt uns die letzten beiden Objekte des Arrays zurück.

$array | select –first 2

Gibt uns die ersten beiden Objekte des Arrays zurück.

Von bis Angaben? Geht auch:

$array[2..3]

oder

(ls c:\windows)[2..3]

2.3.8.4.1    Elemente aus einem Array löschen

Jetzt wollen wir wieder etwas aus dem Array entfernen, aber wie ohne Methoden? Trick 17!

$array=$array[0..1]+$array[3..4]

Wir definieren $array einfach neu und erzählen Ihm, dass nur die Bestandteile des alten Arrays von 0 bis 1 und von 3-4 enthalten sein sollen. Position 2 haben wir damit herausgeschnitten. J

Das hätte auch funktioniert:

$array[0,1,3,4]

Aber das leider nicht:

$array[0..1,3..4]

Sie können also nur die von bis Schreibweise mit den Pünktchen werden, oder die einzelnen Objekte in einer durch Komma getrennten Liste. Beide Verfahren kombinieren geht über einen kleinen Trick:

$array[0..1+3..4]

Also statt des , einfach ein + verwenden.

Wie viele Objekte ein Array enthält bekommen Sie wieder mit der Eigenschaft Count heraus:

$array.count

2.3.8.4.2    Multidimensionale Arrays

Mehrdimensionale Arrays brauchen Sie in der PowerShell eigentlich nicht. Wenn Ihnen sowieso schon der Kopf raucht, trinken Sie einen Kaffee und widmen Sie sich danach dem nächsten Abschnitt über Hash und Splatting. Sie könnten stattdessen einfach einen Array in einen Array stecken. Der Vollständigkeit halber sei es aber erwähnt. Da so etwas in der PowerShell nicht vorgesehen ist, wir aber wissen, dass auch der normale Array ein Objekt ist, können wir einfach ein Objekt selbst anlegen (mehr Details dazu im Abschnitt .NET-Objekte):

$multi=New-Object 'object[,]' 10,10

Damit haben Sie ein zweidimensionales Datengrab, ähnlich einer Tabelle angelegt. Jetzt können Sie z.B. ein 1 x 1 aufbauen:

Achtung! Auch hier startet die PowerShell wieder bei 0 zu zählen. Das bedeutet in der Positionsangabe der Variable $multi können Sie Zahlen von 0-9 angeben, da Sie 10 Felder (0,1,2,3,4,5,6,7,8,9=10 Ziffern,Felder,Positionen,Indexnummern) durch die Zuweisung 10,10 vorgenommen haben.

Ohne Schleifen, die wir uns später im Kapitel Mit Skripten arbeiten, ist das allerdings recht mühselig:

$multi[5,6]=30

$multi[8,9]=8*9

$multi

$multi[5,6]

$multi[8,9]

5 x 6 ist 30. 8 x 9 kann ich nicht im Kopf, deshalb habe ich es die PowerShell ausrechnen lassen ;-). $multi gibt das komplette Datenfeld zurück. Mit $multi[5,6] haben Sie dann explizit auf das Datenfeld zugegriffen und den Wert 30 ausgelesen. Wie gesagt, ohne Schleifen wäre es sehr mühselig, nun das gesamte Datenfeld von Hand zu bestücken.

Kommt mehr als ein Objekt über die Pipeline zum nächsten Cmdlet passiert dies auch in Form eines Arrays, da die einzelnen Objekte in ein Array verpackt werden. Starten Sie ein notepad und tippen Sie danach in der PowerShell (nicht im notepad):

(ps | ? {$_.name -like "notepad*"}).gettype()

Hier haben wir es also mit einem Prozess-Objekt zu tun. Starten Sie bitte noch ein zweites notepad und wiederholen Sie den Befehl. Ist es immer noch ein Prozess? Nein! Weil Sie nun zwei notepads haben, werden die in einen Array verpackt.

Probieren Sie nun einmal das hier, sofern Sie nicht noch ein weiteres notepad offen haben, in dem noch etwas wichtiges drin steht:

ps | ? {$_.name -like "notepad*"} | kill

Schwupps! Alle notepad gekillt, weil kill ein Alias auf das Cmdlet Stop-Process ist. Aber wissen Sie welcher Aufwand dahinter steckt? Schauen Sie sich das doch einmal etwas näher an!

Öffnen Sie bitte noch einmal ein notepad. Schauen Sie einmal welche Methoden es gibt:

ps | ? {$_.name -like "notepad*"} | gm

Da gibt e seine Methode kill. Bitte nicht verwechseln mit dem Alias kill! Der Alias hat so direkt nichts der Methode zu tun. Probieren Sie die Methode doch einmal aus:

(ps | ? {$_.name -like "notepad*"}).kill()

Das sollte geklappt haben. Jetzt starten Sie doch bitte mindestens zwei notepads und führen Sie noch einmal ein Get-Member durch und prüfen Sie, ob die Methode kill immer noch da ist. Obwohl Sie nun einen Array haben, zeigt Get-Member die Methoden und Eigenschaften der Array Mitglieder, nicht des Arrays. Versuchen Sie nun bitte noch einmal die notepads zu killen:

(ps | ? {$_.name -like "notepad*"}).kill()

Hmm…da gab es wohl eine Fehlermeldung, aber wieso? Ganz einfach! Am Array haben wir keine Methode kill, sondern nur an den Objekten im Array. Das Cmdlet Stop-Process bzw. der Alias kill packen uns das Array automatisch aus. Wenn Sie aber die .NET-Methoden anwenden möchten müssen Sie sich selbst um das Auspacken kümmern. Nehmen Sie also wenn möglich immer die Cmdlets, statt den .NET-Methoden. Erstens ist es einfacher und zweitens weniger Tipparbeit. Allerdings sollten Sie die Möglichkeit kennen, falls Sie gewisse Dinge nur über das .NET erledigen können, weil es dazu kein Cmdlets gibt.

Schleifen werden normaler Weise im Zusammenhang mit Skripting erklärt, doch wir benötigen Sie in diesem Falle auch schon bei der normalen Kommandoeingabe. Die Schleifenform die an dieser Stelle für uns interessant ist nennt sich ForEach-Object, zu Deutsch „für jedes Objekt“. Der Kürze halber empfehle ich ein Alias foreach zu benutzen:

ps | ? {$_.name -like "notepad*"} | foreach {$_.kill()}

Bis zum foreach sollte alles klar sein. Darauf folgt innerhalb geschweifter Klammern was mit jedem einzelnen Objekt, das über die Pipe kommt geschehen soll. Das interessante an der Geschichte ist, dass wir den Pipelineinhalt (alle Objekte) normaler Weise in $_ haben. Durch das foreach aussen herum wird aber nun $_ zum jeweiligen Prozess. Auf diese einzelnen Prozesse können Sie dann auch die Methode kill anwenden. Das was in der gescheiften Klammer steht passiert also mehrmals hintereinander. Erst wird NotepadNr.1 gekillt, dann NotepadNr.2 usw. bis es keine notepad mehr gibt und alle notepad Prozesse ausgerottet sind.

Ähnlich wäre das mit einer Liste von Dateien in einem Verzeichnis, auf die Sie die Methode Delete anwenden möchten.

2.3.8.4.3    Arrays miteinander vergleichen

Leider kann die PowerShell das genausowenig wie einzelne Elemente im Array löschen. Für die Lösung zu dem Problem sind Sie an dieser Stelle aber wahrscheinlich noch nicht fit genug. Für diejenigen die es sich vielleicht doch schon zutrauen, können die Lösung im Praxisbeispiel: Arrays miteinander vergleichen  im Kapitel mit Skripten arbeiten nachlesen.

Schauen Sie doch einmal was aus den letzten beiden Abschnitten hängen geblieben ist. Sie kennen Methoden des Datentyps String und Sie wissen wie ein Datentyp Array funktioniert. Bringen Sie nun beides zusammen. Erstellen Sie eine Variable mit der Bezeichnung $VollerName mit Ihrem Vor- und Nachnamen als Inhalt. Z.B.:

$VollerName=“Lieschen Müller“

Nun hätte ich gerne in einer Variablen $Vorname den Vorname und in $Nachname den Nachnamen. Aber nicht einfach zuweisen, sondern aus $VollerName automatisiert extrahieren.

Übungsaufgabe: String

2.3.8.5   Besondere Variablentypen: Hash, Splatting und Unterausdrücke

Eine Hash Variable hat nichts mit Verschlüsselung zu tun! Ein Hash ist ähnlich eines Arrays, nur dass Sie den Index benennen können, statt ihn automatisch durchnummerieren zu lassen.

2.3.8.5.1    Hash

Einen leeren Hash können Sie auch ähnlich eines leeren Arrays anlegen:

$hash=@{}

Der Unterschied liegt in den geschweiften, statt runder Klammern. Wenn Sie gleich einen Inhalt zuweisen möchten kann das so aussehen:

$hash=@{„Eins“=1;“Drei“=3;“Zwei“=2}

Der Index ist hier ganz einfach ein Text und der zugeordnete Inhalt sind Zahlen, die dem Indextext entsprechen.

Als erstes schauen wir uns einmal den Datentyp an:

$hash.gettype()

Hier steht der Datentyp nun wieder in der Spalte mit der Überschrift Name, wie bei den anderen Datentypen auch. Der Array bildet hier also die Ausnahme. In vielen anderen Büchern zum Thema PowerShell finden Sie oft den gut gemeinten Rat, den Datentyp so in Erfahrung zu bringen:

$hash.gettype().fullname

Wegen der Ausnahme beim Array möchte ich Ihnen aber eher empfehlen, nur gettype() einzusetzen.

Einen weiteren Eintrag hinzufügen ist ähnlich wie bei einem Array:

$hash+=@{“Vier“=4}

oder noch einfacher:

$hash.Fuenf=5

Auf alle Elemente des Hash zuzugreifen ist wieder genauso wie beim Array:

$hash

Möchten Sie nun aber die Zahl 2 haben geht das einfach so:

$hash.Zwei

Bestimmt haben Sie sich schon gefragt, ob ich nun nicht einmal mehr zählen kann, wenn ich schon beim Array nicht 8x9 im Kopf rechnen kann, da ich den Hash nicht der Reihe nach aufgebaut habe. Das war Absicht! Denn eine Hash Variable kann einen schon zur Verzweiflung treiben. Insbesondere, wenn man versucht Sie zu sortieren.

$hash | sort

Sortiert nämlich gar nichts! Weder den Index, noch die eingetragenen Werte. Ah, Sie sind ja schlau und haben ein tolles Buch gelesen. Ohne den Schalter -Property sortiert er ja nach allem, nur nicht nach der Eigenschaft, die Sie eigentlich sortiert haben möchten. Nun gut, dann probieren wir einmal unser Glück mit –Property und den Spaltenüberschriften:

$hash | sort -Property Name

$hash | sort -Property Value

Hmmm…da zuckt sich wieder nichts. Was machen jetzt die ganz schlauen? Get-Member, gucken wie die Eigenschaft wirklich heißt.

$hash | sort -Property Keys

$hash | sort -Property Values

Ärgerlich! Sind die Keys wirklich der Index?

$hash | select Keys

Scheint so! Aber er will Sie wirklich ärgern! Die Lösung liegt in der Methode GetEnumerator versteckt (da soll einer drauf kommen!):

$hash.getenumerator() | sort –Property Name

bzw.

$hash.getenumerator() | sort –Property Value

Wenn Sie noch einmal ein Get-Member auf $hash durchführen, können Sie dort wo sich in der 3. Spalte Definition und in der Zeile GetEnumerator trifft lesen: System.Collection.IDictionaryEnumerator (Sehr frei ins Deutsche übersetzt: Wörterbuchauflister)

Im Gegensatz zum Array hat der Hash eine Methode um Elemente wieder zu entfernen, wie Sie vielleicht gerade auch beim Get-Member gesehen haben:

$hash.remove("Vier")

Wirft den Index mit der Beschriftung „Vier“ mit samt seinem Inhalt weg.

Auch beim Hash gibt es natürlich wieder eine Eigenschaft Count. Aber ich glaube so etwas und was die Eigenschaft macht, brauche ich Ihnen an dieser Stelle gar nicht mehr auf die Nase binden. ;-)

2.3.8.5.2    Splatting

Machen wir lieber etwas, dass Sie noch nicht kennen, das Splatting! Keine Angst – keine Horrorfilme. Unter Splatting versteht man die Technik, die Parameter für ein Cmdlet in einem Hash zu speichern und bei Bedarf zu übergeben. So können Sie sich z.B. eine Hashvariable namens $exe in dieser Form anlegen:

$exe=@{"Path"=".";"Filter"="*.exe"}

Der Hashvariablen werden also 2 Zuordnungen erstellt. Zum einen, dass Path=. ist und das Filter=*.exe ist. Wenn Sie nun z.B. wissen möchten, ob im aktuellen Verzeichnis exe-Dateien vorhanden sind, können Sie die Hashvariable an Get-Childitem bzw. den Alias ls splatten:

ls @exe

Wichtig dabei ist, dass die Hashvariable nicht mit $exe, sondern mit @exe übergeben wird. Dadurch werden die Index-Benennungen zu Parametern und die Wertezuweisungen zu den Angaben für die Parameter. Das ist im Moment noch nicht so spektakulär, aber wenn Sie einmal immer wiederkehrend eine lange Parameterkette eintippen müssen, werden Sie sich hoffentlich an diese Technik erinnern. Abgesehen davon können Sie die Splatvariablen in einem Profile-Skript hinterlegen, so dass Sie diese jedes Mal wenn Sie eine neue Konsole starten, verfügbar haben. Wie das geht erfahren Sie im Kapitel Scripting und dort im Abschnitt Profil Skripte. Apropos Skript. In einem Script können Sie damit die Parameter natürlich beliebig zusammen bauen bevor Sie diese an ein Cmdlet übergeben.

2.3.8.5.3    Unterausdrücke

Unterausdruck werden ab PowerShell 2.0 verwendet, um Probleme beim Zugriff auf Hash und Arraywerte zu vermeiden.

Ab PowerShell 2.0 kommt es bei Arrays und Hashwerten vor, dass diese in auszugebenden Texten nicht wie erwartet interpretiert werden. Daher müssen Sie ab PowerShell 2.0 gelegentlich mal zu der aus Linux bekannten Kapselungsmethode $($Variable…) greifen. Die offizielle Bezeichnung dafür ist Subexpression bzw. Unterausdruck. Falls Sie die Konsole inzwischen geschlossen hatten, öffnen Sie diese bitte erneut und erstellen Sie wie im vorangegangenen Abschnitt Splatting beschrieben einen Hash in $exe. Versuchen Sie die Eigenschaft eines Hashs auf den Bildschirm zu bekommen:

“Der Wert der Eigenschaft Filter ist: $exe.filter.“

Das war nicht ganz das erwartete Ergebnis, oder? Also versuchen Sie es mit der $() Kapselung:

“Der Wert von `$exe ist: $($exe.Filter).“

2.3.8.6   Konstruierte Variablenbezeichner

Wenn Sie einen Variablenbezeichner bzw. Variablennamen selbst aus Variablen oder Textteilen zusammensetzen möchten, können Sie das mit Hilfe des Cmdlets New-Variable wie folgt tun:

$a="Eins"

$b="Zwei"

New-Variable -Name ("Text"+$a+$b) -Value 10

Dies erstellt eine Variable mit dem Namen $TextEinsZwei und belegt diese mit dem Wert 10.

$TextEinsZwei

Gibt den Inhalt der soeben dynamisch zusammen gesetzten Variable zurück.

So weit, so gut, aber wie lese ich die Variable nun wiederum dynamisch aus? Das ist etwas umständlicher, aber mittels Get-Variable Cmdlet möglich:

(Get-Variable -Name ("Text"+$a+$b)).Value

Vielleicht interessiert es Sie wann Ihr Rechner gestartet wurde? Das können Sie in der Ereignisanzeige herausfinden indem Sie im Systemlog nach der EventID 12 suchen. Von der PowerShell aus müssten Sie dann immer eingeben:

Get-Eventlog –Logname System –EventID 12 | Select TimeWritten

Schicker ware das doch mit einem Spallting in Form von:

Get-Eventlog @Boot | Select TimeWritten

Bauen Sie die Splatting Hash Variable @Boot für den Befehl zusammen.

Übungsaufgabe: Splatting Hash

2.3.8.7   Umgebungsvariablen

Umgebungsvariablen sind keine PowerShell-Variablen! Sie können allerdings von der PowerShell aus auf die Umgebungsvariablen zugreifen. Die Umgebungsvariablen haben ein eigenes PSDrive:

Env:

Um sich alle Umgebungsvariablen anzeigen zu lassen geben Sie:

ls env:

ein. Möchten Sie auf den Inhalt einer bestimmten Umgebungsvariable zugreifen geht das einfach am Beispiel der Path Variable mit:

$Env:Path

Dies hat allerdings nur eine Änderung innerhalb der aktuellen PowerShell-Konsole zur Folge! Wenn Sie die Konsole schließen und eine neue starten ist der Spuk vorbei. Genauso wenig hat dies Auswirkungen auf das System selbst. Sie können also nicht kaputt machen ;-). Wie Sie darüber hinaus dauerhaft Änderungen an Umgebungsvariablen durchführen können, lesen im Buchteil Praxisbeispiele im Kapitel über die Registry im Abschnitt Umgebungsvariablen dauerhaft ändern.

2.3.9   .NET-Objekte

Wie bereits beschrieben ist die PowerShell selbst eine komplette .NET-Entwicklung. Alles was wir hier tun wird über das .NET-Framework zur Verfügung gestellt. Die Cmdlets vereinfachen uns den Zugriff auf das .NET indem Sie uns mit Ihrer einfachen Verb-Noun Form schon fast das Wort aus dem Mund nehmen. Allerdings hat das .NET-Framework unendlich viel mehr zu bieten, als nur ein paar Cmdlets. In diesem Abschnitt bekommen Sie den zweiten Schlüssel zur Macht indem ich Ihnen den direkten Zugriff auf das .NET-Framework eröffne.

2.3.9.1   Zugriff auf .NET-Klassen und Objekte

Erinnern Sie sich noch an die Abfrage, welche PowerShell Version auf dem Computer installiert ist? Zwei Möglichkeiten, entweder $host (eine vordefinierte Variable), oder aber Get-Host. Weil $host weniger Tipparbeit ist, wollen wir an dieser Stelle bei der Variablen bleiben. Geben Sie zur Erinnerung, doch bitte noch einmal $host ein. Dieses Mal schauen Sie sich bitte die Eigenschaft UI einmal näher an. $host.UI enthält nur eine einzelne Eigenschaft RawUI. $host.UI.RawUI wiederum ist schon etwas spannender. Mit ForeGroundcolor und BackgroundColor, aber natürlich auch den weiteren Eigenschaften. Sie stellen fest, dass die aktuelle ForegroundColor Eigenschaft DarkYellow ist. Mit einem Get-Member erfahren Sie in der Spalte Definition zu der Eigenschaft ForegroundColor, dass es einen sogenannten Getter und einen Setter gibt {get;set;}. Ein Get sagt aus, dass wir die Informationen auslesen können. Ein Set sagt aus, dass wir es auch einstellen können. Das es mit dem Set trotzdem nicht immer klappt, auch wenn es hinten dran steht ist Schade, aber teilweise auch offensichtlich. So gibt es Beispielsweise auch Objekte die den eingebauten RAM-Speicher angeben; mit einem Setter! Das wäre doch einmal was, wenn man das einfach einstellen könnte. Aber zurück zu unserer Farbe. Die können Sie, dank des Setters, tatsächlich auch einstellen:

$host.ui.rawui.foregroundcolor=“Red“

Schon ist unsere Schriftfarbe Rot. Wenn Sie wieder weiß haben möchten, können Sie entweder „White“ zuweisen, oder das Microsoft-Weiß: „DarkYellow“ ;-).

Nun wollen Sie bestimmt wissen, welche Farben Sie noch zuweisen können. Eine Möglichkeit wäre ausprobieren. Besser ist allerdings, wenn Sie .NET fragen. Machen Sie noch einmal einen Get‑Member auf $host.ui.rawui. Unter Definition sehen Sie bei der ForegroundColor auf welche .NET-Klasse die Farbe zurückzuführen ist. Richtig: System.ConsoleColor! Mithilfe des .NET-Objekts System.Enum können wir uns eine Auflistung geben lassen, welche Werte an ForegroundColor bzw. System.ConsoleColor übergeben werden können. Wenn Sie direkt ein .NET-Objekt ansprechen möchten müssen Sie es in eckige Klammern setzen.

[System.Enum]

Das ist nun nicht weiter spannend, aber wenigstens haben wir keine Fehlermeldung erhalten. Auch auf .NET-Objekte können Sie den Get-Member anwenden:

[System.Enum] | gm

Da kommt eine ganze Latte an Methoden und Eigenschaften, die uns aber alle nicht interessieren. Denn an dieser Stelle geht es nicht um eine Methode einer Objektinstanz, sondern um eine Methode der Objektklasse. Ja, auch Objektklassen und nicht nur die Objekte selbst können Methoden haben. Z.B. Methoden um aus der Objektklasse ein Objekt zu erstellen. Bei Methoden die an Objektklassen direkt ausgeführt werden können (ohne ein Objekt daraus abzuleiten) spricht man von statischen (fest zugeordneten) Methoden. Um die statischen Methoden zu sehen, müssen Sie Ihr Get-Member etwas aufpimpen mithilfe des Schalter –Static.

[System.Enum] | gm -static

Das sind weniger, aber hier ist genau das dabei, was Sie brauchen, nämlich die Methode GetNames. Um direkt am .NET die Methode aufzurufen, brauchen Sie auch eine etwas andere Syntax, als bisher:

[System.Enum]::GetNames(„System.ConsoleColor“)

Schwupps, schon haben wir alle zulässigen Farbwerte. So, was ist passiert? Sie haben nun direkt auf die .NET-Klasse System.Enum zugegriffen und dort die statische Methode GetNames aufgerufen. Der Methode haben Sie die Information System.ConsoleColor (die .NET-Klasse, die Sie hinter der ForegroundColor Eigenschaft in der Spalte Definition gefunden haben) übergeben. Die Aufgabe von [System.Enum]::GetNames() ist es herauszufinden, welche Werte von einer Eigenschaft angenommen werden. Das hat Sie in diesem Fall ganz vortrefflich gelöst, nur funktioniert dies leider nicht bei jeder x-beliebigen Eigenschaft. Warum?

$a=5

$a.gettype().fullname

[System.Enum]::GetNames(“System.Int32”)

Klar…welche Werte kann denn ein Int32 so haben? Wollen Sie das wirklich alles auflisten? Das klappt also in der Regel nur, wenn eine Eigenschaft nur ein paar wenige Einstellmöglichkeiten hat und zudem von Programmierer eine sogenannte Enumeration angelegt wurde. Aber vielleicht können Sie direkt Int32 fragen, um den gültigen Wertebereich zu ermitteln? Sie erinnern sich wie man eine Variable explizit als Int32 definiert? Ja, richtig mit [Int32]! Warum probieren Sie hier nicht einmal Get-Member mit dem Schalter –Static?

[int32] | gm –static

Aha! Da gibt’s eine Min- und eine MaxValue Eigenschaft. Abfragen? Bitte schön:

[int32]::MaxValue

[int32]::MinValue

2.3.9.2   Komplette .NET-Dokumentation

Sollten Sie einmal mit diesen Tricks nicht weiterkommen und mehr Informationen brauchen, googeln Sie doch einfach einmal nach der .NET-Klasse und klicken Sie den ersten besten Link an, der zu msdn.microsoft.com führt. Probieren Sie das doch gleich einmal mit System.Math aus. Ja, googeln Sie bitte System.Math und klicken Sie den MSDN-Link.

Hier finden Sie nun alle Methoden und Eigenschaft der Klasse System.Math erklärt. Hier ist eigentlich das komplette .NET-Framework dokumentiert. Durch die Suchmaschine sind wir aber gleich auf der Seite die uns interessiert gelandet.

Die Math Bibliothek ist nicht für Drogensüchtige, sondern hier können wir erweiterte Mathematische Funktionen nutzen, wie z.B.:

[Math]::Pi

für die Zahl Pi, oder

[Math]::Sqrt(9)

zum Wurzel ziehen. Die Überschrift Methods (z.B. Sqrt) dürfte klar sein, doch mit Fields sind in dem Fall Eigenschaften (z.B. Pi) gemeint.

2.3.9.3   Bestehende .NET-Objekte erweitern und eigene erstellen

Zeit für etwas Gentechnik und lila Augenfarbe! Hier werden Sie lernen Ihre Objekte um Ihre selbst gebastelten Eigenschaften zu erweitern und sogar eigene Objekte zu erstellen.

2.3.9.3.1    Alternative Namen (Aliase) für Objekt-Eigenschaften hinzufügen

Wenn Sie sich die Ausgabe von Get-Process | Get-Member anschauen stellen Sie fest, dass es eine AliasProperty gibt die statt der eigentlichen Eigenschaft ProcessName auch die Bezeichnung Name zulässt. Sie erinnern sich bestimmt auch noch an die Eigenschaft PSIsContainer bei Dateien und Verzeichnissen, die angibt, ob es sich um einen Ordner oder um eine Datei handelt. Dann wollen wir doch einmal eine AliasProperty an die Objekte eines Verzeichnisinhalts dran kleben:

$Inhalt=ls c:\windows

$Inhalt=$Inhalt | Add-Member -Membertype aliasproperty `
-Name
 Ordner -Value PSIsContainer –PassThru

$Inhalt | gm

ls c:\windows | gm

Zunächst holen Sie sich in der ersten Zeile den Verzeichnisinhalt des Windows-Verzeichnisses ab und hinterlegen es in der Variablen $Inhalt. Keine Angst! Die Veränderungen, die Sie hier durchführen passieren nur in Ihres Objekts, der Variablen $Inhalt. Das Dateisystem, oder selbst das normale Get-Childitem Cmdlet bzw. der Alias ls bleibt dabei unangetastet. In der zweiten Zeile fügen Sie den Objekten aus der ArrayVariable $Inhalt mithilfe des Add-Member Cmdlet ein neues „Mitglied“ hinzu. -MemberType gibt dabei vor, dass es sich um eine AliasProperty handelt. Mit dem Schalter –Name vergeben wir die alternative Bezeichnung und mit –Value die Eigenschaft auf die sich der alternative Name bezieht. Da Add-Member normaler Weise keine Ausgabe erzeugt, Sie aber die Veränderung wieder $Inhalt zuweisen möchten, steht am Schluss noch der Schalter ‑PassThru. Der sorgt bei Cmdlets die keine Ausgabe erstellen dafür, dass Sie das doch tun. Die 3. Zeile beweist mittels Get-Member, dass Sie eine AliasEigenschaft hinzugefügt haben und Zeile 4, dass diese Eigenschaft beim normalen Cmdlet nicht übernommen wurde. Das geht natürlich auch, aber dazu sollten wir uns erst einmal über Skripting (nächstes Kapitel) unterhalten und dann können wir uns die dauerhafte Erweiterung von .NET-Objekten anschauen.

Um nun auf Ihre erste, selbst gebastelte Eigenschaft zuzugreifen, können Sie z.B. mit select darauf zugreifen:

$Inhalt | select name,ordner | ft -auto

Holt Ihnen die Eigenschaften Name und Ordner aus dem Inhalt und damit es ein bisschen schicker aussieht ein Format-Table hinterher. Ihre AliasEigenschaft verhält sich gegenüber allen Befehlen so, als wäre Sie schon immer da gewesen. Sie können Sie also genauso gut einsetzen, wie die Aliaseigenschaft Name aus dem Get-Process Cmdlet.

2.3.9.3.2    Objekte um selbst erstellte Eigenschaften erweitern und Ballast entfernen

Untersuchen Sie doch einmal das Get-Service Cmdlet mit Get-Member. Finden Sie eine Eigenschaft die eine Zeitangabe macht? Nein, warum auch? Normaler Weise will man den aktuellen Stand der Dienste damit abfragen und eine Historie wird nicht geführt. Vielleicht möchten Sie aber dennoch gerne wissen, wann ein Dienst gelaufen ist und wann nicht. Wäre doch toll, wenn Sie das Get-Service und das Get-Date Cmdlet irgendwie mixen könnten. Willkommen im Genlabor!

Vom Get-Service interessieren uns an dieser Stelle nur der Name und der Status.

$TimeService=Get-Service | select Name, Status

Ein Get-Member auf $TimeService offenbart Ihnen, dass nur noch die Eigenschaften Name und Status in Ihrer ArrayVariable $TimeService enthalten sind. Das Verfahren ist ähnlich der AliasProperty, nur hier verweisen Sie nicht einfach auf eine andere Eigenschaft im selben Objekt, sondern schieben ein ganzes Zeit Objekt hinein.

$TimeService=$TimeService | Add-Member -Membertype `
noteproperty ‑Name
 Zeit -Value (Get-Date) –PassThru

$TimeService |gm

$TimeService

In dieser schlanken Form können Sie das nun sehr gut exportieren. Es hat aber den Nachteil, dass Sie auch die Methoden entfernt haben, sodass Sie auf einen der gelisteten Services kein Start oder Stop Befehl mehr senden können. Wenn Sie es nicht exportieren möchten, aber die Methoden behalten, dann lassen Sie bei der Definition von $TimeService einfach die Pipe mit dem select weg, denn der select schneidet nicht nur die nicht erwähnten Eigenschaften, sondern auch die Methoden raus.

Falls Ihnen der Aufwand in diesem Fall zu hoch erscheint, können Sie solche angepassten Listen auch auf andere Weise mit dem select Alias direkt zusammenstellen:

$TimeService=Get-Service | select `
Name
,Status,@{Label=“Zeit“;Expression={Get-Date}}

Bis zu Get-Service | select sollte soweit alles klar sein. Name, Status übernimmt aus dem Get-Service Cmdlet nur die beiden Eigenschaften Name und Status. Dann folgt noch ein Komma und danach die 3. Eigenschaft. Aber, was ist das? In dem immer gleichen Konstrukt @{Label=;Expression={}} können Sie selbst direkt weitere Eigenschaften einfügen. Nach dem=hinter Label legen Sie dabei die Spaltenüberschrift, bzw. die neue Eigenschaftenbezeichnung fest. Alternativ können Sie statt Label= auch Name= schreiben, falls Ihnen das besser gefällt. Bei Expression dürfen Sie in die geschweiften Klammern reinschreiben was Sie möchten, Hauptsache die PowerShell könnte das auch so auf der normalen Kommandozeile interpretieren. Anstatt des Get‑Date Cmdlets, kann hier auch eine komplette Befehlskette, oder einfach nur “Lemmi was here“ stehen. Bei Befehlsketten dran denken: Soll die Ausgabe des vorhergehenden Cmdlet zur Eingabe des nächsten werden ist die Pipe zu verwenden, sollen aber mehrere Befehle hintereinander ausgeführt werden, deren Eingaben nicht verkettet werden sollen ist das Semikolon zu verwenden. PowerShell erleichtert uns das Anpassen von Objekten also wieder ungemein.

2.3.9.3.3    Externe Programme ausführen und Ihr erstes eigenes Objekt

Da wir das Skripting noch nicht besprochen haben wird Ihr erstes eigenes Objekt zunächst einmal nur Eigenschaften enthalten und keine Methoden. Das Objekt soll den Namen Ihres PCs und die IPv4-Adresse enthalten. Um den Namen des PCs herauszubekommen bedienen Sie sich des externen Programmes hostname, dass Sie sicherlich von anderen Shells her kennen. Um ein externes Programm wie hostname aufzurufen bedarf es keinerlei Verrenkungen, sondern wir können es einfach direkt eintippen und mit Enter bestätigen, wie auch die ganz normalen PowerShell Cmdlets:

hostname

Schon wissen Sie wie Ihr Rechner heißt. Wenn Sie nun aber externe Programme mit internen oder anderen externen Kommandos kombinieren möchten müssen Sie sich allerdings bewusst werden, was da im Hintergrund abläuft:

Hinweis: Weil ich IPv6 aktiviert habe kommt in der Rückmeldung ::1 statt 127.0.0.1.

Hier versuchen Sie ein externes Programm ping in Kombination mit noch einem externen Programm hostname zu verwenden. Die erste Variante schlägt fehl, aber die zweite funktioniert. Warum? In der ersten Variante interpretiert die PowerShell hostname nicht als externes Kommando, sondern als Rechnername den wir anpingen möchten. Um nun erst das externe Programm hostname auszuführen und den Namen der Arbeitsstation zu erhalten, müssen Sie es in runde Klammern setzen. Die Bash-Fans kennen das in der Form: $(Befehl der zuerst ausgeführt werden soll), oder: ` Befehl der zuerst ausgeführt werden soll`. Dadurch wird uns der Name zur Arbeitsstation geliefert und an ping übergeben. Dann versucht ping die Arbeitsstation mit dem tatsächlichen Namen und nicht eine Station mit dem Namen hostname zu erreichen.

Externe Kommandos haben allerdings den Nachteil gegenüber PowerShell Cmdlets ziemlich dumm zu sein, da sie nicht Objekt orientiert arbeiten, sondern einfach nur Texte ausspucken. Bei einem einzelnen Wert, wie ihn hostname liefert ist das nicht weiter tragisch. Allerdings bei ping oder IPConfig sind wir wieder in der Shell-Steinzeit angelangt, bei der wir mit wilden Textoperationen die Informationen herausschneiden müssen :-P.

Die PowerShell hat zum Glück das Cmdlet Test-Connection als ping Ersatz, das Ihnen die gewünschte Objektorientierung zur Verfügung stellt. Durch das externe Programm hostname bekommen Sie den Namen und durch Test-Connection bekommen Sie die IP-Adresse zum Namen. Allerdings müssen Sie da noch ein wenig filtern, da Test-Connection Ihnen viel mehr Infos liefert, als Sie hier haben möchten.

Auch hier sorgen Sie mit der runden Klammer um hostname, dass zunächst der Name des eigenen Rechners abgeholt wird. Der Schalter –count 1 sorgt dafür, dass nur ein einzelnes ICMP Paket verschickt wird. Das select schließlich sorgt dafür, dass wir nur die IPv4-Adresse erhalten.

Jetzt haben Sie Kenntniss über die Voraussetzungen wie Sie sich die beiden Informationen für Ihr Objekt beschaffen. Jetzt geht’s ans Objekt basteln.

Mit dem Befehl New-Object PSCustomObject können Sie eigene Objekte erstellen. Auch hier müssen Sie natürlich einen Ablageort bereitstellen:

$EigenesObjekt=New-Object PSCustomObject

Das war’s eigentlich schon! Ihr erstes Objekt ist geboren. Ab sofort können Sie Get-Member darauf ansetzen:

$EigenesObjekt | gm

Hier sind nun lediglich ein paar Standardmethoden zu sehen, die jedes .NET-Objekt bei der Geburt mit auf den Weg bekommt. Z.B. das schon bekannte GetType(), oder ToString(). ToString will ich an dieser Stelle auch gleich erklären, da wir es gerade von dummen externen Programmen hatten. Wenn Sie von der PowerShell aus externe Programme mit Objekten füttern, kommen die damit nicht klar. Die fressen nur Gras (also Text), kein Haute Cuisine (Objekte). Genau das macht die Methode ToString() für Sie. Die komplette Ausgabe wird von Objekten in Text umgewandelt, bevor es irgendwo weiter geleitet wird. Achten Sie darauf die Kühe auf der Weide nicht mit ganzen Heuballen zu erschlagen, sprich kürzen Sie die Objektinformationen auf die Eigenschaften zusammen, die auch als Text vom externen Programm verstanden werden.

Um nun unsere beiden Eigenschaften dran zu basteln, können Sie wieder auf das im vorangegangenen Abschnitt erklärte Add-Member Cmdlet zurückgreifen. Hier finden Sie eine weitere in diesem Fall etwas kürzere Möglichkeit:

Add-Member –InputObject $EigenesObjekt -MemberType `
NoteProperty
 ‑Name Name -Value (hostname)

Hier pipen Sie nicht, sondern geben über den Schalter –InputObjekt an, welches Objekt Sie erweitern möchten. Der Rest sollte aus dem vorangegangenen Abschnitt bereits klar sein.

Auch um von der IP-Adresse nur den String (den Text) zu bekommen und nicht den gesamten Hash mit der Bezeichnung, können Sie auf die Methode ToString zurückgreifen:

$ip=(Test-Connection (hostname) -Count 1).ipv4address.tostring()

Dran kleben:

Add-Member –InputObject $EigenesObjekt -MemberType `
NoteProperty
 ‑Name IP -Value $ip

Fertig! Wenn Sie mögen, können Sie nun auf die Eigenschaften zugreifen und sich natürlich auch mit Get-Member Ihr Werk anschauen.

Dieses Objekt ist im Moment allerdings recht starr. Ändert sich der Name des Computers, dann bleibt der Name des Computers in Ihrem Objekt erhalten. Es bleibt so immer der Wert im Objekt von dem Zeitpunkt, wo das Objekt um die jeweilige Eigenschaft erweitert wurde.

Um immer den aktuellen Rechnernamen im Objekt zu führen können wir eine ScriptProperty einsetzen. Funktioniert fast genauso:

Add-Member –InputObject $EigenesObjekt –MemberType `
ScriptProperty
 ‑Name DynamicName -value {(hostname)}

Die Unterschiede sind gelb hervorgehoben. Also zum einen ändert sich der Membertype und zum anderen muss der auszuführende Code wie für einen Skriptblock üblich in geschweifte Klammern eingefasst werden. In diesem Beispiel könnten sogar die runden Klammern entfallen. Bei dieser Form wird also jedes Mal, wenn die Eigenschaft abgefragt wird das Skript in den geschweiften Klammern ausgeführt.

Noteproperties können auch jeder Zeit „verunglimpft“ werden. Sie können nun ja über:

$EigenesObjekt.IP

direkt die IP abrufen. Sie können jetzt allerdings auch hergehen und so etwas machen:

$EigenesObjekt.IP=“Das wird schwer zu pingen“

Das entspricht nun natürlich keiner „üblichen“ IP-Adresse mehr.

ScriptProperties hingegen können Sie so einfach nicht klein bekommen. Probieren Sie doch einmal:

$EigenesObjekt.DynamicName=“Hutzli Butzli“

Es sollte die Fehlermeldung:

Der Set-Accessor für die DynamicName-Eigenschaft ist nicht verfügbar.

erscheinen. Eine ScriptProperty kann also so einfach nicht zerstört werden.

2.3.10                     Kontrollfragen

Wie können Sie herausfinden der wie vielte Tag des Jahres gerade ist?

Wieviele Prozesse laufen gerade auf Ihrem System?

In welcher Variable finden Sie den Pipelineinhalt?

Was ist der Unterschied zwischen Measure-Command und Measure-Object?

Wie filtern Sie aus dem Windows-Verzeichnis alle *.exe Dateien, die größer sind als 100KB?

Was ist der Unterschied zwischen den Vergleichs-Operatoren –eq, -like und –match?

Welche Datentypen (Arten von Variablen) kennen Sie?

Was ist der Unterschied zwischen einem Array und einem Hash?

Wie nennt sich das Vorgehen um durch einen Hash Parameter an ein Cmdlet zu geben?

Wenn Sie einen Array oder Hash in einem Text wiedergeben möchten, worauf sollten Sie achten?

Wie können Sie auf Umgebungsvariablen zugreifen?

Wo finden Siedie Dokumentation zum .NET-Framework?

Wie ist die grundlegende Syntax um auf ein .NET-Objekt zuzugreifen?

Mit welchem Cmdlet können Sie .NET-Objekte erweitern?

Einige Cmdlets erzeugen keine Ausgabe. Mit welchem Parameter können diese Cmdlets überreden doch eine Ausgabe zu erstellen?

Wie nennt sich das Cmdlet, um neue Objekte zu erstellen?

Was müssten Sie eingeben um die installierten Updates des Betriebssystems mit im Objekt zu haben?

Link zu den Lösungen für Objektorientierung

2.3.11                     WMI Objekte

Die WMI-Schnittstelle eröffnet uns ein weiteres objektorientiertes Framework ähnlich dem von .NET, aber etwas älter, was nicht heißen soll, dass es nicht mehr gepflegt wird. Windows Management Instrumentation (WMI) ist die Microsoft Version des WBEM-Standards. WBEM steht für Web based Enterprise Management. D.h. Sie haben es hier mit einem Hersteller übergreifenden Standard zu tun bei dem weit über 200 Firmen, wie z. B. auch Cisco, DELL, Novell, Oracle und HP mitmachen. Das bedeutet wir können also von WMI aus z.B. auch einen Cisco Switch verwalten. Auch das gute alte NT 4.0 könnten Sie über WMI ansprechen, ohne dass dort PowerShell oder .NET installiert sein muss. In diesem Kapitel gibt es den dritten Schlüssel zur Macht, der den Weg auch zu anderen Systemen ebnet.

2.3.11.1                Theorie

WMI nutzt RPC für die Kommunikation über das Netzwerk. Möchten Sie also auf einen entfernten Rechner per WMI Zugreifen müssen Sie dafür sorgen, dass eventuell vorhandene Firewalls den Port 135 über TCP geöffnet haben und die Kommunikation durch lassen.

WMI gliedert sich in verschiedene Namespaces (Namensräume). In diesen Namespaces befinden sich bereits die Objekte. Der oberste Einsprung Punkt in WMI auf einem PC nennt sich root. Darunter befinden sich Bereiche die von PC zu PC unterschiedlich sein können. Die angebotenen Namespaces sind abhängig vom jeweiligen Betriebssystem, sowie der installierten Anwendungen. Für reine Windowssysteme ist dies jedoch 2.rangig, da sich das Meiste sowieso unter dem Standard Namespace \\.\Root\CimV2 abspielt. Für die Neugierigen unter Ihnen: Mit dem Punkt der noch vor Root steht, ist der lokale Rechner gemeint. Wenn Sie auf einen anderen Rechner zugreifen möchten brauchen Sie nur den Punkt gegen Namen des gewünschten Remoterechners auszutauschen. Mit PowerShell geht das aber alles viel eleganter, wie Sie gleich lesen werden. Apropos Zugriff auf andere Rechner. Wie steht es eigentlich mit der Sicherheit? Wird sich an dieser Stelle der ein oder andere fragen. Die Sicherheit bzw. die Berechtigungen können Sie mittels WmiMgmt.msc eingestellen:

In der Regel brauchen Sie sich hierüber aber keine Gedanken machen. Die Sicherheitseinstellungen von WMI sind von Haus aus als sicher einzustufen. Benutzer können WMI-Informationen auslesen, während Administratoren auch Methoden ausführen können. Ein Reboot des PCs z. B. kann also standardmäßig  nur von einem Administrator durchgeführt werden.

2.3.11.2                WMI-Namensräume und Objektklassen auflisten

Eine Liste der verfügbaren Namensräume erhalten Sie mit:

Get-WmiObject -Namespace root -Class "__namespace" | ft name

oder kurz mit Alias und gekürzten Schalternamen:

gwmi –name root “__namespace” | ft name

Eine vollständige Liste der Namespaces inklusive der Unterklassen ergeben sich mit:

gwmi -Namespace root -List -Recurse | Select -unique __namespace

Anhand der aufgelisteten Namen können Sie bestimmen, ob der jeweilige Namensraum vielleicht für Sie interessante Informationen und Methoden bereithält. Dies ist jedoch nur dann wichtig, falls Sie nicht im Standardnamensraum \root\CimV2 fündig wurden. Die Angabe zum Namespace kann komplett entfallen, wenn Sie auf den Standardnamensraum zugreifen. Um die Objektklassen eines Namespace auflisten zu lassen können Sie einfach:

gwmi –name root\Microsoft –list

eingeben. Falls Sie sich die Liste der Objektklassen des Standardnamespace root\CimV2 anschauen möchten, darf die Angabe des Namespace komplett entfallen:

gwmi –list

Ich glaube jetzt wissen Sie die Bedeutung der Worte: „Hier spielt die Musik!“. Wenn möglich gehen Sie doch einmal an einen anderen PC und geben dort wie hier ein:

(gwmi –list).count

Damit haben Sie die verfügbaren Objektklassen nur (!) im Namespace \root\CimV2 gezählt. Und ich möchte fast wetten, dass auf beiden PCs, selbst wenn Sie zur selben Firmenumgebung gehören, trotzdem eine unterschiedliche Anzahl von WMI-Objektklassen auftaucht. Wenn Sie keine Lust haben zu dem anderen PC hinzulaufen, dann verwenden Sie doch einfach den Schalter –ComputerName oder kurz –computer. Unter PowerShell 1.0 hatten nur recht wenige Cmdlets diesen Schalter, aber von Version zu Version werden es mehr, sodass Sie die meisten Cmdlets einfach unter Angabe dieses Schalters auf einem anderen PC ausführen lassen können. Beispiel:

gwmi –list –computer RemoteRechnerName

Die Liste ist, wie Sie gesehen haben ziemlich lang. Aber wir können einen Suchfilter einsetzen. Die meisten Objektklassen die unter Windows-Betriebssystemen Daten liefern beginnen mit Win32_. Wenn Sie also Objektklassen finden möchten, die etwas mit Festplatten zu tun haben können Sie z.B. eingeben:

gwmi -list win32_*disk*

Schon kommt nur noch eine Liste mit Objektklassen, die etwas mit Disk zu tun haben. Alles was mit Software zu tun hat z.B.:

gwmi -list win32_*software*

2.3.11.3                WMI-Objekte abfragen

Bei den Festplatten werden Sie schon beim Eintrag Win32_LogicalDisk interessante Informationen vorfinden:

gwmi win32_logicaldisk

Hier finden Sie nun mehrere Objektinstanzen (Laufwerk C:, D:, usw.) der Objektklasse Win32_LogicalDisk. Da WMI ebenfalls Objekt orientiert arbeitet, können Sie alle Cmdlets wie gewohnt auch auf die WMI-Objekte anwenden. Wollen Sie sich das Objekt Laufwerk C: etwas genauer anschauen, empfiehlt es sich zunächst einmal, dass Ganze drum herum zu entfernen:

gwmi win32_logicaldisk | ? {$_.DeviceID –eq „C:“}

Auch hier sehen wir zunächst einmal nur ein paar Eckdaten, wie Größe des Datenträgers und freier Speicher. Mit select * wird das gleich viel interessanter:

gwmi win32_logicaldisk | ? {$_.DeviceID –eq „C:“} | select *

Unter anderem erfahren wir hier z.B. in der Eigenschaft Filesystem (die Sie ohne select * nicht gesehen haben), ob das Dateisystem NTFS, FAT oder was auch immer ist. Gerade bei WMI-Objekten ist select * sehr zu empfehlen, um nicht wie bei Get-Member nur die Bezeichnungen der Eigenschaften zu sehen, sondern auch gleich was drin steht, da hier die Beschriftungen manchmal in etwas in die Irre führen (z.B. Name, VolumeName, SystemName … ganz schön viele Namen!). Wenn man dann gleich sieht was sich hinter der jeweiligen Bezeichnung verbirgt, ist man meisten schon ein ganzes Stückchen schlauer und stochert nicht planlos im Dunkeln.

Um die Methoden zu sehen führt aber nach wie vor kein Weg an Get-Member vorbei:

gwmi win32_logicaldisk | gm

Um nur die Methoden ohne die Eigenschaften anzuzeigen, können Sie bei Get-Member auch den Schalter –Membertype Methods angeben, oder gekürzt:

gwmi win32_logicaldisk | gm -m methods

Hier finden Sie nun ein chkdsk, aber kein defrag. Ein defrag gibt es doch bestimmt auch irgendwo?! Nun, wenn wir mit dem Suchbegriff disk nicht weiterkommen, dann vielleicht mit volume?

gwmi –list win32_*volume*

Aha! Und dann:

gwmi win32_volume | gm -m methods

In WMI gibt es fast alles, was das Herz begehrt, Sie müssen nur etwas kreativ beim Suchen sein und dürfen nicht gleich aufgeben.

2.3.11.4                WMI-Objekte benutzen

Schauen Sie doch zunächst einmal, ob Ihre Festplatte überhaupt ein Defrag nötig hat. Zunächst schauen wir erst einmal bei win32_volume in die Eigenschaften:

gwmi win32_volume | select *

Dabei stellen Sie fest, dass es hier im Gegensatz zu Win32_LogicalDisk gar keine DeviceID Eigenschaft gibt, nach der Sie Ihre Auswahl auf Laufwerk C: beschränken können! Hier können Sie aber nach der DriveLetter oder Name Eigenschaft filtern. Sie sehen also noch einmal, wie wichtig es ist sich erst einmal Beschreibungen mit Inhalt ausgeben zu lassen und nicht voller Frust, weil DeviceID nicht funktioniert, irgendwann einfach genervt aufgeben. Keine Vermutungen anstellen – rein schauen!

(gwmi win32_volume | ? {$_.Driveletter -eq "C:"}).defraganalysis()

In der ersten runden Klammer holen wir uns die Objektinstanz zu Laufwerk C:, der WMI-Objektklasse Win32_Volume. Auf dieses Objekt (Laufwerk C:) führen wir dann die Methode defraganalysis() aus und finden in der Eigenschaft DefragRecommended einen Wahrheitswert True oder False. Zu Deutsch: Defragmentieren von Laufwerk C: empfohlen: Ja(=True)/Nein(=False).

Wenn Sie hier ein True bekommen, möchten Sie vielleicht auch gerne gleich defragmentieren. Dazu schauen Sie sich aber bitte erst noch einmal kurz die Definition der Methode an:

gwmi win32_volume | ? {$_.Driveletter -eq "C:"} | gm -m methods defrag

Da steht nämlich ganz am Schluss hinter Defrag in der runden Klammer ein wichtiger Hinweis. Die Methode erwartet hier die Angabe des Datentyps System.Boolean (also ein $true oder $false). Das ist zwar auf den ersten Blick ein bisschen dämlich, da Sie die Methode defrag aufrufen können, um Ihr dann mit $false zu sagen: Schlaf weiter! Aber die Defraganalyse liefert Ihnen ebenfalls einen Wahrheitswert. Sie könnten also gleich beide ineinander verschachteln, sodass wenn die Analyse sagt, dass ein Defrag empfohlen wird, gleich auch das Defrag ausgeführt wird. Z.B. so:

$Laufwerk=(gwmi win32_volume | ? {$_.Driveletter -eq "C:"})

$Laufwerk.defrag($Laufwerk.defraganalysis())

Manche Dinge, wie z.B. ein Reboot erfordern besondere Privilegien. Die Befehlskette:

(gwmi win32_operatingsystem).reboot()

bringt Ihnen die Fehlermeldung:

Ausnahme beim Aufrufen von "Reboot": "Recht wurde aufgehoben. "

In solchen Fällen sollten Sie zu dem Schalter –EnableAllPrivileges greifen. Falls Sie auf Ihrem PC noch irgendetwas sichern/speichern möchten, wäre jetzt ein guter Zeitpunkt! Denn der nächste Befehl macht tatsächlich, was er vermuten lässt:

(gwmi win32_operatingsystem -EnableAllPrivileges).reboot()

Denken Sie mal an den Schalter –ComputerName. Da kann man auch so etwas schreiben:

(gwmi win32_operatingsystem –EnableAllPrivileges –Computer Rechner1,Rechner2,Rechner3,usw).reboot()

Sie müssen dazu auf den Zielsystemen nur zur Gruppe der lokalen Administratoren gehören. Handelt es sich um ein modernes Betriebssystem, dass Sie durchstarten möchten, können Sie natürlich auch ganz frech die Cmdlets Restart-Computer bzw. Stop-Computer verwenden ;-). Das eben gelistete Beispiel würde aber sogar bei einem alten Windows NT 4.0 klappen.

WMI-Eigenschaften lassen sich leider nicht so einfach einstellen:

(gwmi win32_logicaldisk -enableallprivileges| ? {$_.DeviceID -eq `
"C
:"}).VolumeName="System"

Dazu müssen Sie auf Set-WmiInstance oder den Alias swmi zurückgreifen. Da dieses Cmdlet aber erst ab PowerShell 2.0 zur Verfügung steht lesen Sie dies bitte im 2. Buchteil Unterschiede zwischen PowerShell 1.0, 2.0 und 3.0 und dort im Kapitel PowerShell 2.0 im Abschnitt Allgemeine zusätzliche Befehle der Version 2.0 – WMI Eigenschaften einstellen nach.

2.3.11.5                WMI-Zugriff ohne Active-Directory

Achtung! Auch bei WMI ist es erforderlich, dem System auf das man zugreifen möchte über die WSMan-Konfiguration mittels TrustedHosts wie unter Remoting in einer Arbeitsgruppe beschrieben zu vertrauen, auch wenn Sie gar kein WinRM nutzen.

2.3.12                     Kontrollfragen

Welcher Port muss für WMI-Abfragen auf anderen Rechnern geöffnet sein?

Können WMI-Abfragen auch auf NT 4.0 Systeme durchgeführt werden?

Wie nennt sich der für Windows Systeme wichtigste Namespace?

Wie können Sie die sichere Konfiguration von WMI überprüfen?

Wie erfahren Sie welche Namespaces auf einem System zur Verfügung stehen?

Wie erfahren Sie auf welche Objektklassen Sie in einem Namespace zugreifen können?

Link zu den Lösungen für WMI Objekte

2.3.13                     COM Objekte

Die COM-Schnittstelle ist auch älter als .NET und wird ebenfalls noch rege eingesetzt, z.B. bei den Microsoft-Office Produkten. Hier erhalten Sie u. a. die Möglichkeit Excel-Dokumente in Excel zu öffnen oder zu bearbeiten, oder auch einen Browser zu starten, der gleich eine vorgegebene Website ansurft. Dies ist der vierte und letzte Schlüssel zur Macht. Hiernach steht Ihnen die komplette Anwendungsprogrammierung offen, mit der Sie jede beliebige Aufgabenstellung meistern können. Das einzige was uns C Programmierer noch voraushaben sind Treiberprogrammierung und etwas Verarbeitungsgeschwindigkeit.

2.3.13.1                COM-Objektklassen auflisten

Ähnlich wie bei WMI hängt das vom installierten Betriebssystem und den Anwendungen ab. Ist kein Excel auf dem Rechner installiert, können Sie natürlich auch kein Excel starten. Das Hauptproblem bei COM-Objekten dürfte sein, in Erfahrung zu bringen, welche Objektklassen auf einem PC zur Verfügung stehen, es hierfür leider keinen Befehl wie Get-WmiObject –List gibt. Auf der MSDN Website (http://msdn.microsoft.com/en-us/library/aa911706.aspx - leider in Englisch) von Microsoft findet man allerdings einen kleinen Hinweis, wie man aus COM-Objektklassen Objektinstanzen erstellt. Alle Programme werden in der Registry unter HKEY_Classes_Root\CLSID mit einer eindeutigen Nummer eingetragen. Bei einigen dieser Schlüssel gibt es für die COM‑Objektklassen einen nicht mehr zwingend eindeutigen, aber sehr viel sprechenderen Unterschlüssel, als diese „wilde“ CLSID. Das sind die ProgIDs. Zum Glück werden die und nicht die CLSID für die Objektinstanziierung verwendet. Mithilfe des Regedits danach zu suchen kann, schon etwas eklig werden. Lassen Sie das doch die PowerShell für Sie machen:

ls REGISTRY::HKEY_CLASSES_ROOT\CLSID -include PROGID -recurse `
| foreach {$_.GetValue("")}

Dies erstellt eine Auflistung aller COM-Objektklassen. Auf 64-Bit-System empfiehlt sich zusätzlich auch nochmal hier nachzuschauen:

ls REGISTRY::HKEY_CLASSES_ROOT\Wow6432Node\CLSID\ -include PROGID -recurse | foreach {$_.GetValue("")}

Das ist ähnlich wie bei Get-WmiObject –List eine recht lange Liste. Auch das Zusammenstellen kostet unsere PowerShell etwas Gehirnschmalz (braucht Zeit). Wie wäre es, wenn uns das einfach in eine Variable packen?

$COMObjekte= ls REGISTRY::HKEY_CLASSES_ROOT\CLSID `
-include PROGID
 -recurse | foreach {$_.GetValue("")}

Jetzt können Sie darin schön schnell nach interessanten Dingen suchen:

$COMObjekte | Select-String Explorer

$COMObjekte | Select-String Excel

$COMObjekte | Select-String Outlook

$COMObjekte | Select-String Paint

$COMObjekte | Select-String Voice

Ihrer Phantasie und Kreativität sind natürlich keine Grenzen gesetzt, nach was Sie suchen. Einfach nur den letzten Begriff austauschen und Sie werden schon etwas finden. Wenn auf Anhieb nichts dabei ist, probieren Sie ähnliche Begriffe.

2.3.13.2                COM-Objekte benutzen

Zunächst müssen Sie sich ein Objekt aus der Objektklasse erstellen. Bei den Beispielen zum Auflisten haben Sie bei dem Suchwort Explorer sicher auch den InternetExplorer gesehen. Machen wir uns von der Objektklasse InternetExplorer doch zunächst einmal ein Kopie zum benutzen (Objekt instanziieren):

$IE=New-Object –com InternetExplorer.Application.1

Das funktioniert also fast genauso wie bei .NET-Objekten, nur mit dem Schalter –com gefolgt von der Klassenbezeichnung. Nun haben Sie einen Internet Explorer gestartet. Wie, Sie glauben mir nicht? Würde ich auch nicht! ;-) Aber schauen Sie doch mal im Taskmanager unter dem Reiter Prozesse. Sehen Sie, dort so etwas wie iexplore.exe? Schon, aber Sie glauben mir immer noch nicht. Hmm, na gut. Schauen Sie sich doch einmal Ihr „tolles Gespenst“ mit Get-Member an:

$IE | gm

Da gibt es eine Eigenschaft Visible (welche fast alle Anwendungen der grafischen Oberfläche haben).

$IE | select Visible

Sichtbar = Falsch! Dann probieren Sie doch einmal ganz frech:

$IE.visible=$true

Wow! Wenn Sie jetzt in den Taskmanager schauen, stellen Sie fest, dass der Prozess nun auch als Anwendung geführt wird. Sie haben gerade den Internet Explorer gestartet und sichtbar gemacht. Wenn Sie wollen können Sie ihn auch wieder unsichtbar machen (z.B. wenn Sie erst in aller Ruhe alles vorbereiten wollen, bevor Sie dem Benutzer das Ergebnis anzeigen). Wie? Na kommen Sie, dass können Sie nun schon selbst!

Surfen wir doch einmal eine bestimmte Website mithilfe einer Methode an:

$IE.Navigate(„http://www.martinlehmann.de“)

Sie brauchen wieder eine Beschreibung, was man mit den Methoden und Properties so anstellen kann? Googeln Sie nach InternetExplorer.Application.1 und in diesem Fall (weil eine Microsoft-Anwendung) finden Sie die Beschreibung wieder beim erst besten Link zu MSDN.

Setzen Sie dem Spuck ein Ende mit:

$IE.quit()

Was ist eigentlich SAPI.SpVoice.1 aus der Liste vorhin? Schauen Sie doch einmal, oder besser: Hören Sie doch einmal! Ich hoffe Sie haben eine Soundkarte eingebaut und die Lautsprecher eingeschaltet, wenn nein, entweder mit dem nächsten Kapitel Mit Skripten arbeiten weiter, oder: Soundkarte rein, Treiber installieren und Lautsprecher an.

$Voice=New-Object –com SAPI.SpVoice.1

$Voice.speak(“Good morning my dear”)

$Voice | gm

Die liebe Anna kann leider nur Englisch gut aussprechen und ist die einzige standardmäßig installierte Stimme. Wenn Sie aber z.B. Microsoft MAPS, die Navigationssoftware installiert haben, stehen Ihnen auch Deutsche Sprecher zur Verfügung, die Sie dann mit der Eigenschaft Voice ändern können. Viel Spaß beim erkunden!

2.3.14                     Kontrollfragen

Wie können Sie sich die auf Ihrem System verfügbaren COM-Objektklassen auflisten lassen (Sie dürfen nachschauen ;-)?

Wie instanziieren Sie aus einer COM-Objektklasse ein Objekt?

Wenn Sie COM-Anwendungen auf der grafischen Oberfläche starten, aber die Anwendung nicht zu sehen ist, woran könnte das liegen?

Link zu den Lösungen für COM Objekte

2.4     Mit Skripten arbeiten

Bislang haben wir Befehle immer direkt an der Konsole eingetippt. An der einen oder anderen Stelle haben Sie sich sicher schon gewünscht das Ganze lieber in einer Datei niederzuschreiben und diese ablaufen zu lassen damit, wenn man sich einmal vertippt hat, nicht noch einmal alles abtippen muss. Des Weiteren will man ja nicht immer wieder denselben Sermon abtippen, wenn man etwas auf mehreren Rechnern machen muss oder zu verschiedenen Zeiten. Dem Umstand möchte ich mit diesem Kapitel über Skripte abhelfen.

2.4.1   Grundlagen zur Skriptausführung

Aus Sicherheitsgründen ist die Ausführung von Skripten in der PowerShell nicht möglich. Da ist wohl noch das Brandmal vom Loveletter (einem VBS-Virus) drauf. Wenn das Skripting abgeschaltet ist, können u.U. auch einige Module (Befehlserweiterungen) nicht geladen werden. Mehr zu Modulen finden Sie im Buchteil Unterschiede zwischen PowerShell Versionen 1.0, 2.0 und 3.0 im Kapitel PowerShell 2.0 und dort im Abschnitt Module in Version 2.0. Bei Windows Server 2012 R2 ist Microsoft mutiger geworden und erlaubt standarmäßig die Ausführung von Skripten die auf der lokalen Festplatte liegen (Execution Policy = RemoteSigned).

PowerShell Skripte sind ganz normale Textdateien mit der Dateinamenerweiterung ps1. PowerShell Skripte können Sie daher mit einem ganz normalen Texteditor wie notepad schreiben. Für gehobenere Ansprüche können Sie auf das Integrated Scripting Evironment (ISE) zurückgreifen. Dies enthält einen Debugger und auch Syntax-Highlighting, allerdings keine Kommandovervollständigung in Version 2.0. In Version 1.0 fehlt das Tool komplett, in Version 2.0 muss es ggf. über die Featureverwaltung im Servermanager hinzugefügt werden und in Version 3.0 haben Sie sogar Kommandovervollständigung. Diejenigen, die noch unter Version 2.0 oder 1.0 arbeiten müssen, können auf das kostenlose Tool von Quest namens PowerGUI zurückgreifen. Das hat schon seit eh und je diese Features an Bord und noch vieles andere mehr, wie z.B. die Anzeige von Variableninhalten.

In den folgenden beiden Unterabschnitten erfahren Sie zunächst die technischen Hintergründe der Skriptausführung.

2.4.1.1   Ausführungsrichtlinie für Skripting abfragen und aktivieren

Um herauszufinden ob PowerShell Skripting auf dem System aktiviert ist, können Sie ein Cmdlet bemühen:

Get-ExecutionPolicy

Dies liefert Ihnen wahrscheinlich Restricted (zu Deutsch: eingeschränkt – bedeutet: Skripte werden nicht ausgeführt) als Ergebnis. Das ist die Standardeinstellung bis einschließlich Windows 8.1. Ausnahme ist Windows Server 2012 R2. Auf Windows Server 2012 R2 ist die Standardeinstellung RemoteSigned.

Wo es ein Get- gibt ist ein Set- oft nicht weit. Mittels

Set-ExecutionPolicy unrestricted

können Sie die Ausführung jeglicher Skripte zulassen.

Alternativ könnte man noch RemoteSigned oder AllSigned zuweisen. RemoteSigned bedeutet, dass Skripte, die sich auf der Festplatte des lokalen Rechners befinden ohne Umschweife ausgeführt werden können, währen Skripte die „Remote“ abgespeichert sind eine digitale Signatur benötigen, um ausgeführt zu werden. Remote bedeutet in diesem Falle Skripte die im Internet, auf einem Fileserver, aber auch auf einer DVD liegen. Bei AllSigned müssten auch die auf der lokalen Festplatte liegen digital signiert sein.

Auch per ActiveDirectory Gruppenrichtlinien (GPO) können Sie die Ausführungsrichtlinie für Computer ebenso wie für Benutzer festlegen. Die Einstellungen finden Sie unter Administrative Vorlagen\Windows-Komponenten\Windows PowerShell\Skriptausführung aktivieren.

Es gibt allerdings noch wesentlich mehr Möglichkeiten, wie ein Blick mit

Get-Help Set-ExecutionPolicy

verrät.

Um ein Skript digital zu signieren benötigen Sie ein digitales Zertifikat, das extra für den Zweck der Codesignatur ausgestellt wurde. Ein Zertifikat mit dem Zweck E-Mail zu verschlüsseln ist hier nutzlos. Das bedeutet weiterhin, dass Sie einen Zertifikatsserver benötigen, der Ihnen dieses Zertifikat ausstellt und dem muss Ihr PC auch noch das Vertrauen aussprechen. Vielleicht schreibe ich dazu auch einmal einen 1000-Seiten Wälzer, aber machen Sie sich nicht zu viel Hoffnung. Ein IT-Fachbuch, ist schließlich kein Harry Potter, der sich millionenfach verkauft. Das hier schreibe ich auch nicht um Geld zu verdienen, sondern weil ich Spaß am Thema PowerShell habe. Wenn Sie tatsächlich ein Zertifikat zur Codesignatur Ihr Eigen nennen, können Sie mit den folgenden beiden Zeilen eines Ihrer PowerShell Skripte signieren:

$cert=Get-PfxCertificate C:\Zertifikatsdatei.pfx

Set-AuthenticodeSignature -Filepath C:\IhrPowerShellSkript.ps1 `
-Cert $cert

Die erste Zeile holt sich das Zertifikat mit dem Privaten Schlüssel zur Code Signatur direkt aus der Zertifikatsdatei von Laufwerk C:\ in die PowerShell Variable $cert. Die zweite Zeile nutzt durch den Schalter –Cert das Zertifikat aus Ihrer Variablen $cert um Ihr PowerShell Skript IhrPowerShellSkript.ps1 auf Laufwerk C:\ digital zu unterschrieben, dass es von Ihnen stammt.

Ich schätze einmal, wenn Sie den nächsten Abschnitt gelesen haben, schlagen Sie sich die Idee sowieso gleich wieder aus dem Kopf.

2.4.1.2   Skriptschutz umgehen

Nun, ich will hier nicht zu viele Angaben machen, sondern nur wie Sie sich selbst helfen können. Aber wenn Sie 2 Ecken weiterdenken, werden Sie ein ernsthaftes Problem entdecken.

Auf Ihrem eigenen Rechner können Sie jederzeit mit Set-ExecutionPolicy Cmdlet den Skriptschutz aus- und wieder einschalten. Aber was, wenn Tante Frieda anruft und Sie Ihr mal eben schnell ein PowerShell Skript schicken möchten, dass die Probleme auf dem Rechner von Tante Frieda beseitigt? Tante Frieda schreibt mit Adler-Suchsystem und braucht auch sonst ziemlich lange, um irgendwas an Ihrem PC zu machen. Ganz zu schweigen davon, wenn Sie etwas von Ihr wissen möchten, wie z.B. ob Sie ein 32, oder 64 Bit Office-Paket einsetzt. Es gibt sogar Gerüchte, dass Sie, als Sie noch berufstätig war einen Helpdesk Mitarbeiter in den Freitod getrieben hat ;-). Wäre doch nett, wenn Sie irgendwie auf Tante Friedas Computer irgendwie den Skriptschutz umgehen könnten:

powershell –executionpolicy unrestricted –file `
C
:\IhrPowerShellSkript.ps1

Dadurch wird ein PowerShell Prozess mit Ihrem Skript unter Umgehung der Ausführungsrichtlinie des jeweiligen PCs gestartet. Sollte das irgendwann einmal nicht mehr funktionieren, können Sie zu einem fiesen Trick greifen:

Type C:\IhrPowershellSkript.ps1 | powershell –

Mit dem DOS-Befehl Type können Sie den Inhalt einer Textdatei (in dem Fall Ihr PowerShellSkript) auslesen. Mit der Pipe wir die Ausgabe an das zu startende Programm (powershell.exe) weitergeleitet. Das Minus hinter powershell bewirkt, dass der PowerShell Prozess seine Eingaben von der Standardeingabe liest. Das ist dann ungefähr so, als ob Sie Ihr Skript gerade selbst in die PowerShell Konsole eintippen würden. Da soll es Leute geben, die sich tatsächlich die Mühe machen PowerShell Skripte digital zu signieren. !-)

Leider habe ich, da ich diese Zeilen schreibe, noch keine Möglichkeit (z.B. GPO zur Ausführungsrichtlinie oder AppLocker) gefunden, etwas gegen böswillige Menschen zu tun, die in der Lage sind 2 Ecken weiter zu denken.

Für die nachfolgenden Beispiele sollten Sie die Skriptausführung zulassen. Ob Sie nun gleich Unrestricted oder RemoteSigned zuweisen überlasse ich Ihrem scharfen Verstand.

2.4.1.3   Ausführungsrichtlinienbei PowerShell Core

Die Standard Ausführungsrichtlinien unter PowerShell Core 6.0 ist RemoteSigned. Auf Windows Systemen kann dies mittles der Datei powershell.config.json unter im Verzeichnis $PSHome angepasst werden. Hierzu ändern Sie Microsoft.PowerShell:ExecutionPolicy z.B. auf Restricted:

{"Microsoft.PowerShell:ExecutionPolicy":"Restricted"}

Unter Linux wird das „Feature“ nicht unterstützt. 3 mal dürfen Sie raten warum man es dort erst gar nicht implementiert hat.

2.4.2   Gutes Skripting

Ein paar warme Worte vorneweg! Alles was Sie an der Kommandozeile eintippen, können Sie auch im Skript verwenden. Doch das sollten Sie nicht! An der Kommandozeile ist es toll, wenn man Aliase nutzt, um schnell zu einem Ergebnis zu kommen. Es ist auch toll Schalter abzukürzen, oder ganz weg zu lassen. Im Moment haben Sie einen bestimmten Alias voll drauf, weil Sie laufend damit arbeiten. Aber ist das in einem Jahr auch noch so? Wenn Sie nach einem Jahr wieder in ein Skript reinschauen, möchten Sie vielleicht ganz gerne auf Anhieb erkennen, was das Skript macht. Wenn Sie einen Alias nach dem anderen verwenden und die Schalter weglassen, war Ihnen vor einem Jahr noch sonnenklar was da passiert ist, aber nun nicht mehr. Sie müssen sich in Ihr eigenes Skript erst wieder einarbeiten. Noch schlimmer, wenn Sie im Team arbeiten. Stellen Sie sich vor, Sie haben da so einen PowerShell Guru als Kollegen und der schreibt nur so was:

gps | ? {$_.name –like „pow*“} | spps

Dann gehe ich einmal stark davon aus, dass er Angst um seinen Arbeitsplatz hat und sich unersetzlich machen möchte. Teamwork hingegen geht so:

Get-Process | Where-Object {$_.name –eq „PowerShell“} | Stop-Process

Beide Zeilen machen genau dasselbe, nur sieht jeder mit PowerShell Grundkenntnissen sofort, was im zweiten Beispiel passiert. Die zweite Zeile ist also vorbildliches Skripting. Zugegeben: ein Where‑Object schreibe ich auch immer als ?, aber ansonsten gebe ich mir schon Mühe es auch für andere leserlich zu verfassen.

$a=read-host „Geben Sie Ihren Namen ein“

Welchen Namen haben wir den nun vom Benutzer bekommen? Den Vor-, oder den Nachnamen? Sein Sie mit Ihren Anweisungen an den Benutzer möglichst präzise, denn die Qualität Ihre Frage bestimmt die Qualität der Antwort, die Sie vom Benutzer erhalten werden. Und wer zum Kuckuck, soll nach einem Jahr noch wissen, dass $a einen Vornamen enthält? Besser wäre folgende Version:

$Vorname=Read-Host –Prompt „Geben Sie bitte Ihren Vornamen ein“

Sie sehen sofort an dem Namen der Variable, was da wohl drin steckt. Vor lauter a,e,i,o,u und x,y,z als Variablenbezeichnung blickt später keiner mehr in einem großen Skript durch. Allerdings sollten Sie es auch nicht übertreiben! Zum einen müssen Sie den Variablenbezeichner jedes Mal wieder eintippen und zum anderen können Sie Ihren Computer manchmal ganz schön verwirren, wenn Sie z.B. Umlaute, wie ö,ä,ü oder Tiefstriche oder sonstige wilde Phantasien einbauen. 26 Buchstaben hat unser Alphabet – das sollte reichen um einen klaren Namen für eine Variable zu finden! Das sollten Sie sich generell auch für alle anderen Benennungen (z.B. Freigaben oder Servernamen) angewöhnen. Auch die Anweisung an den Benutzer ist klar formuliert. Das Zauberwort bitte ist in kurzen Anweisungen auch gut, sollte aber nicht nerven. Groß- u. Kleinschreibung zur Hervorhebung einzelner Worte ist nett, aber nicht so wichtig, wie auf Aliase zu verzichten und Parameter auszuschreiben.

Schreiben Sie reichlich Kommentare, so wie im nachfolgenden Abschnitt beschrieben.

Das sind Tipps, um Arbeitszeit zu sparen. Ob das Skript nun 3 oder 7 Sekunden benötigt kann Ihnen eigentlich egal sein. Arbeitszeit ist teurer, als Rechenzeit! Manchmal sind Skripte jedoch Zeit kritisch, dann will ich nicht sagen: „Vergessen Sie all das hier.“, aber lesen Sie auch den Abschnitt über Skripttuning.

2.4.3   Kommentare

Hier finden Sie Informationen wie man in PowerShell Skriptzeilen kommentiert.

Je mehr Sie kommentieren, umso leichter wird es Ihnen später fallen Änderungen vorzunehmen. Ohne Kommentare hat man ein Skript oft schneller neu geschrieben, statt sich wieder einzuarbeiten. Absolutes Minimum sollte sein, dass Sie die Hauptabschnitte und Funktionen Ihres Skriptes kommentieren. Grundsätzlich kann man sagen: Je mehr, desto besser!

Sie können einen Kommentar durch ein führendes # Zeichen in eine ganze Zeile schreiben:

# Mein erstes PowerShell Skript

oder auch nach einem Befehl, oder eine Befehlskette:

ls # Ist ein Alias auf Get-Childitem

ls | select name,length # Holt den Verzeichnisinhalt ab und zeigt davon den Namen und die Größe an

Ein Kommentar wird also generell durch das # Zeichen eingeleitet. Alles was in dieser Zeile danach folgt wird von der PowerShell nicht mehr interpretiert.

2.4.4   Einfache Parameterübergabe an Skripte

Um ein Skript mit Informationen zu versorgen, kann man einem Skript beim Start gleich einige Dinge mitteilen. Wie das funktioniert, wird nachfolgend erläutert.

Schreiben Sie mit einem Editor Ihrer Wahl (z.B. Notepad oder ISE=Integrated Scripting Environment) folgende Zeilen (die Nummern dienen der Orientierung was zu einer Zeile gehört und sollten nicht mit eingegeben werden!):

1   "Ihrem Skript wurde als erster Wert: $($args[0]) übergeben und ist vom Typ $($args[0].gettype())."

2   "Ihrem Skript wurde als zweiter Wert: $($args[1]) übergeben und ist vom Typ $($args[1].gettype())."

3   "Ihrem Skript wurde als dritter Wert: $($args[2]) übergeben und ist vom Typ $($args[2].gettype() | select BaseType)."

4   "Insgesamt hat Ihr Skript diese Werte erhalten:"

5   $args

6   '$args ist vom Typ: '+($args.gettype() | select BaseType

Nachdem Sie das Skript unter der Bezeichnung Parameter, mit der Dateinamenerweiterung ps1 (spezielle PowerShell Skripteditoren wie ISE und PowerGui machen das automatisch, aber nicht notepad) abgespeichert haben, können Sie das Skript entsprechend aufrufen:

./Parameter.ps1 ABC 123 (ls)

Wenn Sie in dem Verzeichnis stehen indem das Skript liegt, müssen Sie ebenfalls wieder aus Sicherheitsgründen das Skript mit einem einleitenden ./ aufrufen. Sie können gerne die Anfangsbuchstaben des Skripts einsetzen und die Tab-Taste drücken. Dadurch wird nicht nur der Name automatisch vervollständigt, sondern auch gleich das ./ automatisch vorangestellt. Wenn das Skript nicht im aktuellen Verzeichnis liegt, müssen Sie den Pfad (z.B.: C:\IhrVerzeichnis\Parameter.ps1 ABC 123 (ls)) davor schreiben.

Die Variable $args ist wiederum eine besondere Variable, die automatisch die übergebenen Parameter enthält. $args ist immer ein Array, auch wenn nur ein einzelner Wert übergeben wird. Demzufolge haben Sie also in $args[0], den ersten übergebenen Wert, in $args[1], den zweiten, etc…Greifen Sie auf $args ohne eckige Klammer zu, bekommen Sie alle Werte auf einen Schlag. Lesen Sie ggf. noch einmal im Kapitel Objektorientierung den Abschnitt über Besondere Variablentypen: Array.

2.4.5   Im Skript den Skriptpfad herausfinden

Wenn Sie zu einem Skript, weitere Dateien benötigen, z.B. .ico Dateien um Icons anzuzeigen und diese im Verzeichnis des Skriptes selbst ablegen, stellt sich oft die Frage, wo denn ein Benutzer der das Skript und die zugehörigen Dateien überhaupt abgelegt hat. Wenn er das Skript nun auch noch von einer anderen Position aus startet, statt dem Verzeichnis, in dem das Skript liegt, werden Sie schnell feststellen, dass ein Aufruf mit .\DateiImVerzeichnisDesSkripts.txt nicht funktioniert.

Um in einem Skript zu ermitteln, wo das Script überhaupt liegt, können Sie die Systemvariable $MyInvocation abfragen, oder um genau zu sein:
$MyInvocation.MyCommand.Definition

bzw.

$MyInvocation.MyCommand.Path

Ab PowerShell 3.0 geht es noch einfacher:
$PSScriptRoot

Häufig findet man im Internet den Hinweis auf $MyInvocation.InvocationName. Dies liefert aber unter Umständen auch einfach nur einen Punkt, wenn man das Skript aus dem aktuellen Verzeichnis heraus startet.

2.4.6   Kontrollfragen

Welche Dateinamenerweiterung muss ein PowerShell Skript haben?

Ein Skript auf Ihrem Rechner läßt sich nicht starten. Woran liegt das? Wie können Sie das prüfen? Und wie können Sie das Problem beheben?

Sie möchten gerne ein Skript auf einem anderen Rechner ausführen. Wie können Sie sicherstellen, dass das Skript dort läuft, egal welche Ausführungsrichtlinie eingestellt ist?

Wie können Sie in einem Skript einen Kommentar schreiben?

In welcher Variable können Sie in einem Skript übergebene Parameter auslesen und von welchem Typ ist die Variable?

Wie greifen Sie auf das 2. übergebene Element dieser Variable zu?

Link zu den Lösungen für Skripting: Einführung

2.4.7   Schleifenfunktionen

Im Kapitel über Schleifen finden Sie Skript Anweisungen, wie bestimmte Dinge mehrfach durchgeführt werden können, ohne dass Sie den Code dazu mehrmals eintippen müssen.

2.4.7.1   Foreach

Mit ForEach haben Sie ja bereits im Kapitel Objektmethoden in der Praxis und dort im Abschnitt Besondere Variablentypen: Array Bekanntschaft gemacht. Dort haben wir ForEach direkt in einer Pipeline verwendet. ForEach kann aber auch anders:

1   $ObjektArray=Get-ChildItem C:\Test

2   ForEach ($Objekt in $ObjektArray) {

3    Remove-Item $Objekt.Fullname

4   }

Speichern Sie dieses Skript als ps1 Datei ab. Bevor Sie es laufen lassen, sollten Sie allerdings den Inhalt des Ordners Test auf Laufwerk C: an eine andere Stelle kopieren, falls Sie dort noch wichtige Sachen drin haben, die Sie gerne behalten möchten! Das Skript löscht den Inhalt des Test-Ordners.

In der ersten Zeile wird der Verzeichnisinhalt des Verzeichnisses C:\Test in der Array-Variablen $ObjektArray hinterlegt. In Zeile zwei startet die ForEach Anweisung mit dem öffnen der geschweiften Klammer und zieht sich bis zum Ende des Skripts in Zeile 4. In der 3. Zeile wird schließlich die jeweilige Datei gelöscht. Der Übersichtlichkeit halber rückt man den Inhalt der Schleife entweder mit einem Leerzeichen oder einem TabStop etwas ein. Dieser Bereich wird auch Schleifenkörper oder Schleifenrumpf genannt. Der grüne Bereich ist der sogenannte Schleifenkopf. Hier legen Sie fest, aus welchem Array die einzelnen Objekte generiert werden sollen und wie die einzelnen Objekte innerhalb des Rumpfes als Variable angesprochen werden können. Die Variable für das einzelne Objekt und die Arrayvariable werden dabei durch das Zauberwörtchen in getrennt. Um die Datei zu löschen, müssen Sie die Eigenschaft Fullname des Dateiobjekts verwenden, welche den Pfad und den Namen zur Datei enthält. Ein einfaches Remove-Item auf das Objekt selbst funktioniert an dieser Stelle leider nicht. Die Grundlegende Syntax einer ForEach Anweisung sieht so aus:

1   ForEach (Umwandlung eines ObjektArrays in einzelne Objekte) {

2    Anweisung, was mit den Objekten passieren soll

3    Falls Sie mehrere Anweisungen zu schreiben haben, können Sie diese gerne einfach untereinander wegschreiben.

4    So lange bis die geschweifte Klammer geschlossen wird, wird all das wiederholt bis kein Objekt mehr übrig ist.

5   }

In der runden Klammer steht im Skript, dass die einzelnen Dateien und Verzeichnisse aus der Array-Variablen innerhalb der geschweiften Klammern in die Variable $Objekt gelegt werden. Der Code innerhalb der geschweiften Klammern wird mehrfach hintereinander ausgeführt. Um genau zu sein, für jede Datei und jedes Verzeichnis innerhalb von C:\Test genau einmal. In der Variablen $Objekt ist dann die jeweilige Datei bzw. das Verzeichnis hinterlegt und wird durch den Aufruf von Remove‑Item $Objekt entsprechend gelöscht.

Der Übersichtlichkeit halber rückt man die Anweisungen in der geschweiften Klammer etwas ein. Sie können aber auch alles in eine Zeile quetschen:

ForEach ($Objekt in $ObjektArray) {Remove-Item $Objekt.Fullname}

Selbstverständlich können Sie auch innerhalb eines Skriptes die Pipelinevariante mit $_ verwenden:

Get-ChildItem C:\Test | ForEach {Remove-Item $_.Fullname}

In diesem Fall kann der Schleifenkopf komplett entfallen, da hier die Variable $_ die als Array über die Pipe kommt innerhalb des Schleifenrumpfes das jeweilige einzelne Objekt enthält. Für die nächsten Beispiele erspare ich uns immer wieder zu lesen/schreiben, dass Sie die Zeilen in einer Datei speichern sollen, um diese dann auszuführen.

2.4.7.2   ForEach-Object

Am Anfang des Buches habe ich einmal erwähnt, dass es egal ist, ob Sie ForEach oder ForEach‑Object verwenden. Nun, das stimmt nicht ganz. Es gibt einen kleinen, aber feinen Unterschied.

Bei ForEach wird der komplette Schleifen Inhalt für alle Durchläufe am Stück kompiliert und dann ausgeführt. Das führt dazu, dass es in der Regel schneller als Foreach-Object abgearbeitet wird, allerdings jeder Durchlauf einzeln im RAM gehalten wird und so entsprechend mehr Speicher kostet.

Bei ForEach-Object wird jeder Durchlauf einzeln kompiliert, ausgeführt und bei jedem weiteren Objekt beginnt der Spaß von vorne. Dadurch ist es langsamer als ForEach, braucht aber weniger RAM. Ein weiterer Vorteil ist, die „Streaming“-Fähigkeit. Übergibt man also Beispielsweise:

ls | foreach-object {$_.length/1024}

kann von ls die erste Datei direkt über die Pipe übergeben werden und ForEach-Object beginnt sofort mit der Berechnung, während ls dann zur selben Zeit die zweite Datei über die Pipe liefert usw.

Bei

ls | foreach {$_.length/1024}

muss ls zunächst die komplette Liste aller Dateien zusammenstellen und diese dann auf einen Rutsch über die Pipeline übergeben, erst dann kann ForEach mit seiner Arbeit beginnen.

Wenn Sie bei einem Ihrer Skripte einmal Speicherprobleme haben, weil Sie etliche millionen Objekte an ein ein ForEach (statt ForEach-Object) pipen, sollten Sie mal überlegen, ob Sie nicht lieber etwas mehr Tipparbeit leisten.

2.4.7.3   For

Mit ForEach können Sie genau so viele Durchläufe stattfinden lassen, wie Sie Objekte haben. Wenn Sie aber einmal eine bestimmte Anzahl Durchläufe vorgeben möchten ist ForEach keine gute Wahl. Hier eignet sich die For-Schleife wesentlich besser Die Syntax würde so aussehen:

For (Variable mit Anfangswert; So lange diese Bedingung erfüllt ist mache einen weiteren Durchlauf; Zähle den Anfangswert bei jedem Durchlauf um den hier genannten Betrag hoch) {

 Anweisungen wie in der ForEach Schleife, was jedes Mal zu tun ist.

}

Und als Praxisbeispiel:

For ($i=1; $i -le 10; $i++) {

 Write-Host $i

}

Hier wird der Anfangswert für die Zählvariable $i auf 1 gesetzt. Solange $i weniger oder genauso viel wie 10 ist, führe die Anweisungen in der geschweiften Klammer noch einmal aus. Nach jedem Durchlauf zähle $i um 1 hoch. $i fängt demzufolge also bei 1 an zu zählen und schreibt den Inhalt von $i durch die Anweisung Write-Host auf den Bildschirm. Der Zauber ist vorbei, wenn $i nicht mehr kleiner oder gleich 10 ist. Erreicht $i also den Wert 11 wird die Schleife nicht mehr durchlaufen und mit dem Rest des Skriptes (so den einer folgt) fortgefahren.

Das hätten wir auch einfacher haben können:

1..10

hätte den gleichen Effekt gehabt. Allerdings hätten Sie so keine komplexen Anweisungen damit ausführen können, wie Sie es in der For-Schleife machen können. Um 1.5 könnten Sie auch nicht erhöhen bei jedem Durchlauf. Bei der For-Schleife könnten Sie allerdings statt $i++ wie im Beispiel auch $i=$i+1.5 schreiben.

2.4.7.4   While

Die While-Schleife prüft noch vor dem ersten Durchlauf, ob die Bedingung erfüllt ist. Die grundlegende Syntax sieht so aus:

While (so lange diese Bedingung wahr ist durchlaufe die Schleife in den geschweiften Klammern) {

 Hier stehen die wieder die Anweisung was bei jedem Durchlauf zu tun ist.

 Zusätzlich sollte hier auch gezählt werden, da in den runden Klammern keine Zählmöglichkeit gegeben ist.

}

Um nun wieder von 1 bis 10 zu zählen könnte das so aussehen:

$i=1

While ($i –le 10) {

 Write-Host $i

 $i++

}

Die erste Zeile erstellt die Variable $i und weißt Ihr den Wert 1 zu. In den runden Klammern wird geprüft ob $i weniger oder gleich 10 ist. So lange das der Fall ist werden die Anweisungen in den geschweiften Klammern ausgeführt. In Zeile 3 wird der Inhalt von $i auf den Bildschirm gebracht. Danach, in Zeile 4, wird $i um 1 erhöht. Nach der Zeile 4 Beträgt der Wert von $i also 2. Zeile 5 beendet den Abschnitt der zu wiederholenden Anweisungen womit wir wieder am Anfang der Schleife in Zeile 2 stehen und erneut geprüft wird, ob $i immer noch weniger oder gleich 10 ist. Da $i nun 2 ist wird der Schleifenrumpf (der Teil mit den geschweiften Klammern) noch einmal wiederholt. Das Spielchen geht immer so weiter bis $i als Wert 10 auf dem Bildschirm kommt. Danach wird $i auf 11 hochgezählt und wieder zum Schleifenkopf (die Anweisung in der runden Klammer) gesprungen. Dort wird festgestellt, dass $i nun nicht mehr kleiner oder gleich 10 ist. Daher wird nun der Schleifenrumpf nicht mehr durchlaufen und im restlichen Skript weiter gemacht. Da nach der Schleife nichts mehr folgt ist das Skript damit beendet.

2.4.7.5   Do While

Wenn Sie eine Schleife mindestens 1 mal durchlaufen möchten, können Sie zu dieser Form greifen, da hier die Überprüfung der Bedingung erst am Ende der Schleife erfolgt. Hier steht also Ausnahmsweise der Schleifenkopf am Ende, nach dem Schleifenkörper. Mit dem Schlüsselwort Do wird die Schleife eingeleitet:

Do {

 Gleiches Spiel wie bei While

} While (so lange diese Bedingung wahr ist, durchlaufe die Schleife in den geschweiften Klammern)

Zählen Sie wieder bis 10:

$i=1

do {

 Write-Host $i

 $i++

} While ($i –le 10)

Um den Unterschied zu verdeutlichen hier noch 2 Beispiele, zunächst ohne Do am Anfang:

$i=11

While ($i –le 10) {

 Write-Host $i

 $i++

}

Und mit Do:

$i=11

do {

 Write-Host $i

 $i++

} While ($i –le 10)

Sie sehen, obwohl die Bedingung der Schleife von Anfang an beim 2. Beispiel nicht erfüllt war wurde die Schleife einmal durchlaufen.

2.4.7.6   Do Until

Until ist ähnlich der While-Schleife nur anders herum. Sprich die Until-Schleife wird so lange durchlaufen, bis die Bedingung erfüllt ist.

Do {

 Gleiches Spiel wie bei While

} Until (so lange diese Bedingung nicht wahr ist, durchlaufe die Schleife in den geschweiften Klammern)

Auch hier wieder das praktische Beispiel um von 1 bis 10 zu zählen:

$i=1

do {

 Write-Host $i

 $i++

} until ($i –gt 10)

Until-Schleifen müssen immer mindestens einmal durchlaufen werden, da es von Until keine Form ohne einleitendes Do gibt, spricht Sie dürfen die Schleife nicht mit Until einleiten.

2.4.7.7   Verschachteln von Schleifen

Schleifen können ineinander geschachtelt werden, wie dieses 1x1 Beispiel zeigt:

For ($x=1;$x –le 5;$x++) {

 For ($y=1;$y –le 5;$y++) {

  Write-Host ”$x x $y=“($x*$y)

 }

}

In der inneren Schleife wird $y von 1 bis 5 gezählt. In der äußeren Schleife steht $x zunächst auf 1. Ist die innere Schleife mit $y einmal komplett durchlaufen, zählt die äußere Schleife $x um eins hoch und startet die innere Schleife erneut, sprich $y wird erneut von 1 bis 5 durchgezählt. Das Spielchen geht so weiter bis $x der äußeren Schleife auf 6 hochzählen würde und somit die Bedingung nicht mehr erfüllt. Das Write-Host im Kern der beiden Schleifen zeigt zunächst die Werte von $x und $y an und dann noch den berechneten Wert aus der Multiplikation von $x mit $y.

2.4.8   Kontrollfragen

Sie möchten gerne von 50 bis 5 in Abständen von 0,5 herunterzählen. Welche Schleifenform ist dafür am besten geeignet? Erstellen Sie die Schleife und lassen Sie die Werte zur Überprüfung am Bildschirm anzeigen.

In einem Temp-Verzeichnis auf Ihrer Festplatte befindet sich eine unbestimmte Anzahl von Dateien. Sie möchten diese gerne löschen. Welche Schleifenform verwenden Sie und wie würde diese Schleife aussehen?

Wie können Sie eine Endlosschleife erzeugen?

Was ist der Unterschied zwischen der while und der do-while Schleife?

Link zu den Lösungen für Skripting: Schleifen

2.4.9   Bedingte Ausführung

Im Bereich des bedingten Ausführens werden Sie Anweisungen kennenlernen, die Ihnen ermöglichen Programmabschnitte nur unter bestimmten Voraussetzungen durchlaufen zu lassen. Also ganz einfach ausgedrückt: Wenn die Ampel grün ist fahre über die Kreuzung und wenn sie rot ist halte an.

2.4.9.1   If, Else und Elseif (Wenn-Dann-Sonst - Abfragen)

Mit If (zu Deutsch: Wenn) können Sie einen Programmabschnitt nur dann durchlaufen, wenn die formulierte Bedingung wahr ist. Am praktischen Beispiel:

$Ampel=“Grün

If ($Ampel –eq “Grün“) {

 Write-Host “Die Ampel ist grün.“ –Foregroundcolor green

}

If ($Ampel –eq ”Gelb“) {

 Write-Host ”Die Ampel ist gelb.“ –Foregroundcolor yellow

}

If ($Ampel –eq ”Rot“) {

 Write-Host ”Die Ampel ist rot.“ –Foregroundcolor red

}

Wie Sie sehen, wurde nur der Text in grün angezeigt. Der gelbe und rote Text aber nicht. In der runden Klammer wird abgefragt, ob die Bedingung wahr ist. Ähnlich wie bei den zuvor beschriebenen Schleifen wird der Inhalt der geschweiften Klammern nur dann ausgeführt, wenn die Bedingung in den runden Klammern wahr ist. Da Sie der Variablen $Ampel in der ersten Zeile den Wert grün zugewiesen haben, stimmt nur die Bedingung des ersten If Vergleichs. Wenn Sie mögen ändern Sie doch einmal in der ersten Zeile die Zuweisung z.B. auf das Wort Gelb und lassen Sie Ihr Skript erneut ablaufen.

Etwas eleganter und kürzer würde das Ganze mit Else und ElseIf aussehen:

$Ampel=“Rot

If ($Ampel –eq „Grün“) {

 Write-Host „Die Ampel ist grün.“ –Foregroundcolor green

} ElseIf ($Ampel –eq „Gelb“) {

 Write-Host „Die Ampel ist gelb.“ –Foregroundcolor yellow

} Else {

 Write-Host „Die Ampel ist rot.“ –Foregroundcolor red

}

Hier wird unser Text nun in Rot ausgegeben. Die erste If Bedingung $Ampel=“Grünist nicht erfüllt, da Sie dieses Mal in der ersten Zeile die Zuweisung Rot getroffen haben. Also entfällt schon einmal der grüne Text. Else zu Deutsch heißt andernfalls. Da bei Gelb hierauf direkt ein If folgt bedeutet das so viel wie „Wenn die Ampel schon nicht grün ist, dann prüfe einmal ob sie vielleicht gelb ist“. Da Sie aber auch nicht Gelb ist, wird auch der gelbe Text nicht angezeigt. Da Verkehrsampeln nun einmal nur drei Farben haben und sie weder grün noch gelb ist, muss sie wohl folglich rot sein. Das abschließende Else bewirkt also in allen anderen Fällen: Zeige an, dass die Ampel rot ist.

Nun ja, die Ampel könnte vielleicht auch kaputt sein, oder einen Stromausfall haben, also gar keine Farbe.

Übungsaufgabe: Überlegen Sie doch einmal wie sie das mit PowerShell formulieren könnten. Die Lösungen finden Sie im Anhang Lösungen im Abschnitt Skripte. Auch wenn Sie meinen die Lösung zu kennen, schauen Sie doch einmal rein, da ist noch ein anderer Tipp versteckt.

2.4.9.2   Verschachteln von Bedingungen

Auch Bedingungen können geschachtelt werden.

For ($x=1;$x –le 2;$x++) {

 For ($y=1;$y –le 2;$y++) {

  If ($x –eq 1) {

   Write-Host “X ist Eins!

   If ($y –eq 1) {

    Write-Host “Y ist Eins!

   }

  }

 }

}

Böses Beispiel! Da muss man ja denken ;-). Ganz außen herum haben Sie zwei verschachtelte Schleifen. Die innere zählt $y von 1 auf 2 hoch und die äußere $x von 1 auf 2. Das ergibt also 2 x 2=4 Durchläufe des inneren Schleifenrumpfes mit folgenden Werten:

1.       Runde: $x=1, $y=1

2.       Runde: $x=1, $y=2

3.       Runde: $x=2, $y=1

4.       Runde: $x=2, $y=2

In der ersten Runde stoßen Sie innerhalb der $y Schleife auf eine If-Abfrage, deren Anweisung wird nur ausgeführt, wenn $x=1 ist. Da dies der Fall ist, wird auf den Bildschirm „X ist Eins!“ ausgegeben und weiterhin gefragt, ob $y auch 1 ist. Da dies in Runde 1 der Fall ist, wird auch die Anweisung nach der $y-Abfrage ausgeführt und somit „Y ist Eins!“ ebenfalls angezeigt.

In Runde 2 ist $x immer noch 1, daher wird abermals „X ist Eins!“ angezeigt. $y ist aber 2. Folglich ist die zweit If-Abfrage nicht mehr wahr und somit entfällt der Text „Y ist Eins!“.

In Runde 3 ist $x zu 2 geworden. Das bedeutet, dass die erste If-Bedingung bereits nicht erfüllt ist. Daher entfällt die Ausgabe „X ist Eins!“. Zur Abfrage ob $y=1 ist kommt es nicht, da diese Abfrage innerhalb der geschweiften Klammer der ersten If-Abfrage steht und folglich nur dann überhaupt abgefragt wird, wenn $x=1. Obwohl also $y=1 wird dieser Text hier nicht ausgegeben, weil $x nicht 1 ist.

In der 4. Runde ergibt sich dieselbe Lage wie in Runde 3, da auch hier bereits die erste Anforderung $x=1 nicht erfüllt ist.

Übungsaufgabe: 1 x 1

Versuchen Sie doch einmal ein 1x1 auf dem Bildschirm auszugeben, das ungefährt so aussieht:

Die Lösung finden Sie im Anhang Lösungen und dort im Abschnitt Skripte. Das Beispiel beinhaltet auch wie man einen 2 dimensionalen Array anlegt.

2.4.9.3   Switch

Switch eignet sich hervorragend zu Auswertung einer Menüauswahl.

1   $Eingabe=Read-Host –Prompt “Wählen Sie 1 für ABC, 2 für DEF und die 3 für XYZ“

2   Switch ($Eingabe) {

3    1 {“ABC“}

4    2 {“DEF“}

5    3 {“XYZ“}

6    default {“Bitte geben Sie nur eine Zahl zwischen 1-3 an.“}

7   }

Die erste Zeile holt sich eine Benutzereingabe in die Variable $Eingabe. In der 2. Zeile folgt die Switch Anweisung. Die Switch Anweisung verzweigt hier anhand des in runden Klammern angegebenen Inhalts der Variablen $Eingabe. Von wo bis wo die Switch Anweisung geht markiert die fett gedruckte geschweifte Klammer. In der 3. Zeile steht zunächst welchen Inhalt die unter Switch angegebene Variable haben muss um den nachfolgenden Code in den nachfolgenden geschweiften Klammern auszuführen. Bei 1 wird also ABC angezeigt, bei 2 DEF und bei 3 XYZ. Falls der Benutzer bei der Eingabe geschummelt hat und nicht macht, was Sie Ihm sagen, können Sie mit default alle anderen Eingaben abfangen, die zu keinem der vorigen Abfragen passen.

Bei Switch sind mit dem Schalter –Wildcard auch Platzhalterzeichen erlaubt:

$Person=“Autor: Martin Lehmann“

Switch –Wildcard ($Person) {

 “Bäcker*“ {“Bäcker“}

 “Autor*“ {“Autor“}

 “Arzt*“ {“Arzt“}

 “*Martin*“ {“Martin“}

 “*Markus*“ {“Markus“}

 “*Lehmann“ {“Lehmann“}

 “*Meyer“ {“Meyer“}

}

Dabei werden alle in der Switch Anweisung zutreffenden Bedingungen ausgeführt. Bäcker steht nicht am Anfang der Variable $Person und somit wird die Anweisung in der folgenden geschweiften Klammer nicht ausgeführt. Allerdings beginnt $Person mit den Buchstaben Autor und daher wird die hiernach folgende Anweisung das Wort Autor auf den Bildschirm zu schreiben ausgeführt. Arzt passt auch nicht und wird daher auch nicht ausgeführt. Der Name Martin kommt allerdings wieder in der Variablen vor. Daher wird auch hier wieder die Anweisung ausgeführt. Markus ist nicht Bestandteil der Variable, also keine Anweisung. $Person endet mit Lehmann und es wird auch wieder Lehmann auf den Bildschirm geschrieben. Meyer=Fehlanzeige, deshalb auch hier wieder keine Anweisung in aus der nachfolgend geschweiften Klammer.

2.4.9.4   Schleifen- und Programmsteuerung

Manchmal möchte man eine Schleife, die Verarbeitung einer Bedingung, oder ein komplette Skript gerne vorzeitig beenden. Dafür gibt es verschiedene Befehle.

2.4.9.4.1    Endlosschleifen

Eine Endlosschleife, können Sie ganz einfach dadurch herstellen, indem Sie eine Bedingung für das Schleifenende formulieren, die immer bzw. nie zutrifft. Je nachdem ob Sie mit While oder Until arbeiten. Solche Schleifen, oder auch wenn Ihr Skript hängen bleibt, können Sie manuell mit Strg+C abbrechen.

While (1 –eq 1) {

 “Autsch! Drücken Sie Strg+C um das Skript zu beenden.“

}

1 wird wohl für immer und ewig 1 bleiben. Also wird der Schleifenrumpf in den geschweiften Klammern immer wieder aufs Neue ausgeführt.

Alternativ wird wahr auch immer wahr bleiben:

While ($true) {

 “Autsch! Drücken Sie Strg+C um das Skript zu beenden.“

}

2.4.9.4.2    Schleifen vorzeitig abbrechen

Mit der Break Anweisung können Sie eine Schleife vorzeitig beenden.

1   “Skriptstart“

2   While (1 –eq 1) {

3    $a=Read-Host -Prompt “Ich nerve so lange, bis Sie endlich eine 1 eintippen“

4    If ($a –eq „1“) {break} Else {“Sie haben $a eingegeben. Also noch mal!“}

5   }

6   “Skriptende – geschafft!“

Hier haben Sie eine Endlosschleife wie im vorigen Abschnitt. Im Schleifenrumpf wird mittels eine If-Anweisung geprüft, ob der Benutzer endlich eine 1 getippt hat. Ist das der Fall wird die Anweisung Break ausgeführt und somit die Schleife verlassen und die letzte Zeile des Skripts angezeigt. Ist das nicht der Fall wird dem Benutzer durch die Else Anweisung erklärt, was er falsch gemacht hat.

Oder als Beispiel mit einer Zählschleife:

“Skriptstart”

$a=0

Do {

 Write-Host $a

 if ($a -eq 5) {break}

 $a++

} While ($a -lt 10)

“Skriptende!”

Obwohl Ihre Schleife laut der While-Abfrage am Schluß bis 9 zählt, ist das Skript schon bei der Zahl 5, wegen der Break-Anweisung, in der letzten Zeile angelangt.

Wenn Sie eine Schleife nicht vorzeitig verlassen möchten, sondern noch einmal von vorne starten ohne den Rest der Schleife zu durchlaufen können Sie das mit der Anweisung Continue erreichen. Bei diesem Beispiel bitte kurz nach dem Start gleich Strg+C drücken.

“Skriptstart”

$a=0

Do {

 Write-Host $a

 if ($a -eq 5) {continue}

 $a++

} While ($a -lt 10)

“Skriptende!”

Kurz habe ich gesagt…was haben Sie denn für eine Reaktionszeit? ;-)

Sobald $a 5 ist kommt es zur Anweisung Continue. Das verursacht den Sprung zum Anfang der Schleife. Dummer Weise kommt es erst nach dieser Abfrage zum hochzählen der Variable $a und somit zu einem erneuten endlosen Schleifendurchlauf.

2.4.9.4.3    Skript beenden

Um ein Skript komplett zu verlassen, statt nur ein Konstrukt (Schleife, Bedingung, etc…), können Sie die Anweisung Exit verwenden.

“Skriptstart”

$a=0

Do {

 Write-Host $a

 if ($a -eq 5) {exit}

 $a++

} While ($a -lt 10)

“Skriptende!”

Den Unterschied zwischen Exit und Break verdeutlicht der Vergleich zwischen diesem Skript und dem Zählskript mit der Break-Anweisung. Die Bildschirmausgabe ist fast identisch. Allerdings fehlt bei dem Skript mit Exit, die Ausgabe aus der letzten Zeile “Skriptende!“, da die Anweisung Exit das Skript und sogar die aktuelle PowerShell Konsole komplett verlässt, während Break nur aus der aktuellen Schleife aussteigt.

Die Exit Anweisung kann auch Statuscodes an das aufrufende Programm senden.

Exit 0                                       bedeutet normaler Weise: Alles in Ordnung
Exit andereZahl             bedeutet das etwas schief gelaufen ist. Die Nummern sind dabei nicht
                                                         vorgegeben. Das aufrufende Programm muss halt etwas mit diesen
                                                         Nummern anfangen können.

2.4.10                     Funktionen

Im Abschnitt über Funktionen lernen Sie PowerShell Cmdlets selbst zu schreiben. D. h. Sie können ganze Programmabschnitte mit einem einfachen „selbstgestrickten“ Befehl ausführen lassen und auch diesen Befehlen noch etwas mitteilen oder sich auch von Befehlen etwas zurückgeben lassen.

2.4.10.1                Einfache Subroutinen

Mit einfachen Subroutinen können Immer wiederkehrende Aufgaben von beliebigen Stellen im Skript aus mit einem einfachen Befehl aufrufen. Bei Schleifen macht man auch immer wiederkehrende Aufgaben, aber an einer bestimmten Stelle im Skript. Wenn Sie z.B. die Mehrwertsteuer für einen Nettobetrag ausrechnen möchten, wollen Sie das vielleicht an mehreren Stellen des Skriptes machen. Ohne Funktionen müssten Sie an Ort und Stelle immer wieder erneut den gesamten Code für die Berechnung schreiben. Bei der Mehrwertsteuer ist das sicher nicht so dramatisch, aber wenn die Berechnungen komplexer werden und mehrere Zeilen Code ausfüllen wäre das schon ganz schön nervig. Um den Einstieg noch weiter zu vereinfachen, werden wir zunächst einfach nur Text anzeigen.

Funktionen müssen immer am Anfang des Skriptes stehen, damit PowerShell sie kennenlernt. Das beudetet aber nicht, dass das was da drin steht auch am Anfang passiert, wie dieses einfach Beispiel zum Einstieg verdeutlichen soll:

“Anfang des Skripts mit der Funktionsdefinition

Function MwSt {

 “Hier würden Sie die Mehrwert Steuer berechnen.“

}

“Ende der Funktionsdefinition und Start des Hauptprogrammes.“

“Erster Sprung zur Mehrwert Steuer Berechnung.“

MwSt

“Dann machen Sie ein paar andere Dinge.“

“Jetzt brauchen Sie noch einmal die Mehrwert Steuer.“

MwSt

“Ende des Skripts“

Durch das Zauberwort Function eingeleitet können Sie einen Namen für Ihr kleines Unterprogramm vergeben. In diesem Falle heißt es MwSt. Was die Funktion tun soll, steht in den nachfolgenden geschweiften Klammern. An der Stelle im Programm wo die Funktion beschrieben steht wird sie aber nicht ausgeführt, sondern nur bekannt gemacht. Jedes Mal wenn Sie den Programmcode der Funktion verwenden möchten müssen Sie einfach nur deren Namen aufrufen. Im Beispielskript wird 2 Mal der Name MwSt genannt und demzufolge auch 2 Mal ausgeführt.

2.4.10.2                Variablen in Funktionen verwenden

Hier soll Ihr Hauptprogramm mit Ihrer Funktion kommunizieren, sprich Werte austauschen. Das dies manchmal ziemlich verzwickt sein kann liegt meistens daran, dass man die Hintergründe nicht kennt. Dem will ich hier abhelfen.

2.4.10.2.1            Grundlagen zur Parameterübergabe an Funktionen und Rückgabewerte von Funktionen

Bleiben wir bei der Mehrwert Steuer. Dieses Mal lassen Sie Ihre Funktion aber auch tatsächlich rechnen:

Function MwSt ($Eingabe) {

 Return $Eingabe*0.19

}

[Double]$Abfrage=Read-Host –Prompt „Geben Sie eine Zahl ein“

"Netto: $Abfrage"

"Mehrwertsteuer: "+(MwSt $Abfrage)

"Brutto: "+($Abfrage+(MwSt $Abfrage))

Die ersten drei Zeilen definieren wieder die Funktion namens MwSt. Dazu gleich weitere Infos. Schauen wir uns aber erst einmal das weitere Skript an. Mit Read-Host holen Sie sich eine Zahl vom Benutzer. Damit es auch wirklich eine Zahl ist, wird die Variable $Abfrage mit [Double] explizit als Kommazahl definiert, da Read-Host selbst bei Zahleneingabe standardmäßig immer einen String-Datentyp übergibt. Nach dieser Eingabe haben Sie in $Abfrage also eine Zahl und somit zeigt die nächste Zeile einfach mit „Netto: “ eingeleitet noch einmal an, was der Benutzer eingetippt hat. Die vorletzte Zeile wird nun interessant. „Mehrwertsteuer: “ wird als Text ausgegeben – kein Hexenwerk. + fügt eine weitere Ausgabe hinzu. Damit Ihre MwSt-Funktion nicht als darzustellender Text oder Parameter fehlinterpretiert wird, müssen Sie an dieser Stelle den Aufruf Ihrer Funktion in runde Klammern setzen. Das Spannende daran ist, dass Ihrem Funktionsaufruf noch die Angabe der Variable $Abfrage folgt. Der in $Abfrage enthaltene Wert wird damit an die Funktion MwSt übermittelt. Jetzt kommen wir zur Funktion. Vor der geschweiften Klammer auf steht in runden Klammern wieder eine Variable mit dem Namen $Eingabe. Der Wert aus der Variablen $Abfrage, der an die Funktion MwSt gesendet wurde wird damit in der Variable $Eingabe entgegen genommen. Innerhalb der Funktion haben Sie also den Nettobetrag in der Variablen $Eingabe. In der zweiten Zeile wird die Mehrwertsteuer ausgerechnet durch $Eingabe*0.19. Das Return davor sorgt dafür, dass das Ergebnis der Berechnung nicht auf dem Bildschirm erfolgt, sondern zum Rückgabewert der Funktion wird. Zusätzlich sorgt die Return Anweisung auch dafür, die Funktion an dieser Stelle zu verlassen. Der berechnete Wert wird also zurück zum Skriptteil geschickt, der die Funktion aufgerufen hat. Zurück im Hauptprogramm wird der Rückgabewert aus der Funktion übernommen. Da wird hier einen Text + einen weiteren Text verketten, jedoch die berechnete Mehrwertsteuer in Form einer Zahl vorliegen haben, baut unsere clevere PowerShell automatisch den numerischen berechneten Mehrwertsteuer Wert in einen Text um, damit er mit dem vorderen Text verknüpft auf den Bildschirm gebracht werden kann. In der letzten Zeile läuft es ähnlich. Hier wird ebenfalls wieder die Funktion aufgerufen und der Nettowert aus $Abfrage ermittelt. Die Funktion liefert den fertig berechneten Wert zurück. Zu der zurückgeliefert Mehrwert Steuer wird noch einmal der Nettobetrag addiert. Beides zusammen konvertiert uns die PowerShell automatisch in einen Text, um ihn mit dem ersten Text „Brutto: “ zu verknüpfen und auf dem Bildschirm darzustellen.

OK, ich habe etwas geflunkert. Die Return Anweisung ist „Schmuck am Nachthemd“. Was in anderen Sprachen unerlässlich ist, kann in der PowerShell entfallen, da alles was Sie innerhalb einer Funktion ausgeben nicht auf dem Bildschirm ausgegeben wird, sondern zum Rückgabewert der Funktion wird. Wenn Sie so wollen, ist die Standardausgabe innerhalb einer Funktion von Bildschirm zum aufrufenden Skriptbefehl verbogen worden. Das nachfolgende Beispiel soll das verdeutlichen:

# Funktion

Function Calc ($x,$y) {

 $x=$x+10

 $y=$y*20

 $x,$y

}

# Hauptprogramm

$l=10;$m=3

$a=Calc $l $m

$a[0]

$a[1]

$f,$g=Calc $l $m

$f

$g

Die Funktion calc erwartet 2 Werte. Der erste wird in $x innerhalb der Funktion hinterlegt und der 2. Wert in $y. calc erhöht $x um den Wert 10 und $y wird mit 20 multipliziert. Nach den zwei Berechnungszeile erfolgt die Rückgabe der 2 Werte $x und $y an den aufrufenden Skriptteil. Das Hauptprogramm definiert und belegt zu Beginn die 2 Variablen $l und $m. $a nimmt die von calc berechneten Werte entgegen. $a ist nur eine Variable, aber calc liefert 2 Werte zurück. Macht nix! PowerShell ist schlau und merkt, dass das wieder einmal nicht passt und definiert $a automatisch als Array, der die zwei integer Werte aufnehmen kann. Einfach nur cool! Auf die beiden Werte kann dann Array typisch getrennt zugegriffen werden: $a[0] und $a[1]. Wem das etwas zu seltsam anmutet, versteht vielleicht die Zuweisung mit $f,$g=calc $l $m etwas besser. Wie Sie sicher gemerkt haben wird innerhalb der Funktion $x,$y nicht auf dem Bildschirm geschrieben!

Fügen Sie innerhalb der Funktion zwischen der Zeile $x,$y und } noch eine zusätzliche Zeile ein:

 $x,$y

 “Hutzli Butzli“

}

Untersuchen Sie anschließend einmal die Variablen $a, $f und $g. $a ist ein Array mit 3 Elementen. $f ist ein Integer und $g ist jetzt auch ein Array. Das $x aus calc wird in $f hinterlegt. $y kommt in $g[0] und der Text “Hutzli Butzli“ landet in $g[1].

Nun schreiben Sie nach “Hutzli Butzli“ doch noch einmal eine Pipe und Out-Host, also so:

 “Hutzli Butzli“ | Out-Host

Starten Sie das Skript. Standardausgabe ist ja innerhalb der Funktion die aufrufende Skriptstelle. Durch das Pipen an Out-Host verbiegen Sie die Ausgabe wieder zurück auf den Bildschirm. Das bedeutet $x und $y sind weiterhin Rückgabewerte der Funktion, während der Text “Hutzli Butzli“ innerhalb der Funktion auf den Bildschirm schreibt. Es gibt also doch noch einen Einsatzzweck für das Out-Host Cmdlet. ;-)

Wenn Sie innerhalb einer Funktion eine Ausgabe unterdrücken möchten, z.B. den Rückgabewert eines .NET Aufrufs, dann finden Sie sehr häufig diese Schreibweise:

 [void] “Hutzli Butzli“

Damit wird die Ausgabe des Textes “Hutzli Butzli“ komplett unterdrückt. Es wird also weder zum Rückgabewert der Funktion, noch wird es auf dem Bildschirm ausgegeben, sondern landet im „Nirwana“. Alternativ wären auch diese beiden Varianten noch praktikabel:

 “Hutzli Butzli“ | Out-Null

oder

 “Hutzli Butzli“ > $null

2.4.10.2.2            Unterschied zwischen Return, Break und Exit

Alle drei sind zum Aussteigen gedacht. Break dient zum Ausstieg aus Schleifen, Return zum Ausstieg aus Funktionen und Exit zum Ausstieg aus Skripten bzw. der aktuellen Konsole. Zunächst einmal zur Return Anweisung ein kleines Beispiel:

Function Call {

 Return "Eins"

 Return "Zwei"

}

Call

Der Text Zwei wird von der Funktion call nicht mehr ausgegeben, da die Return Anweisung vor “Eins bereitsden Ausstieg aus der Funktion herbeiführt.

Probieren Sie auch diese beiden Skripte einmal nacheinander (nicht in eine Datei schreiben!) aus:

# Skript 1

Function Call {

 "Eins"

 Exit

}

Call

"Zwei"

 

# Skript 2

Function Call {

 "Eins"

 Return

}

Call

"Zwei"

Dies macht wohl eindeutig den Unterschied zwischen Exit und Return klar.

So, und nun zu den ganz kuriosen Begebenheiten:

Foreach ($i in 1..5) {$i; If ($i -eq 3){Return}}

1..5 | Foreach {$_; If ($_ -eq 3){Return}}

1..5 | Foreach {$_; If ($_ -eq 3){Break}}

Foreach ist eine Zählschleife. Break ist das Statement um Schleifenkonstrukte abzubrechen – nicht Return! Das kann zwar gut gehen, wie die erste Zeile beweist, aber auch ganz schön in die Hose gehen, wie die zweite Zeile zeigt. Wie man Schleifen korrekt verlässt steht in der dritten Zeile.

2.4.10.2.3            Alternative Möglichkeit zur Parameterübergabe

Sie haben im vorigen Abschnitt diese Schreibweise für Funktionen kennengelernt:

Function Name (1. Wert, 2. Wert, usw.) {Was mit den Werten zu tun ist. -> Rückgabe}

Das ist auch ganz in Ordnung. Allerdings, kann die Zeile bei vielen Werten recht lang und damit unübersichtlich werden. Alternativ steht Ihnen noch diese Schreibweise zur Verfügung:

Function Name {

 Param (

  $VariableFürWert 1,

  $VariableFürWert 2,

  usw.

 )

 Was mit den Werten zu tun ist. -> Rückgabewert.

}

Statt die Parameter also vor der geweiften Klammer in runde Klammern gefasst zu übergeben, wird erst die geschweifte Klammer geöffnet und danach folgen die Parameter wieder in runde Klammern eingefasst. Bei dieser Schreibweise müssen die runden Klammern aber mit dem Begriff Param als sogenannter Parameterblock kenntlich gemacht werden.

2.4.10.2.4            Scopes: Gültigkeitsbereiche von Variablen und Funktionen und deren Vererbung

In diesem Abschnitt lernen Sie etwas über die Gültigkeitsbereiche von Variablen und Funktionen. Dies ist eine wichtige Voraussetzung, um später eigene PowerShell Cmdlets zu schreiben.

Erstellen Sie zunächst ein einfaches Skript:

$Inhalt

$Inhalt=“Skript

$Inhalt

Speichern Sie das Skript ab. Öffnen Sie eine frische PowerShell Konsole und geben Sie ein:

$Inhalt=“Konsole

$Inhalt

Wie erwartet ist in der Variablen $Inhalt das Wort Konsole enthalten. Starten Sie nun Ihr eben geschriebenes Skript. Das hat zunächst einmal den Wert von $Inhalt wiedergegeben. Haben Sie $Inhalt zuvor in Ihrem Skript einen Wert zugewiesen? Nein! Es hat die Variable $Inhalt von der Konsole geerbt. Dann haben Sie in Zeile zwei Ihres Skriptes der Variablen $Inhalt einen neuen Wert zugewiesen und in Zeile drei zu Testzwecken ausgeben lassen. Dabei stellte sich heraus, dass nun der Wert Skript in der Variablen $Inhalt steht. Ist das so? Geben Sie doch nun, nur noch einmal zu Sicherheit $Inhalt auf der Konsole ein. Überrascht? Ja, ich auch ;-).

Wenn von der Konsole aus ein Skript gestartet wird erbt das Skript alle Variablen von der Konsole. Änderungen daran bleiben jedoch im Skript und gehen nicht zurück auf die Konsole. Diese Grafik soll das verdeutlichen:

Wenn Sie auf der Konsole $a und $b mit dem Wert Konsole belegen und dann ein Skript starten, wird automatisch quasi eine Kopie der Variablen unter demselben Namen angelegt. Um das zu verdeutlichen sind die Skriptvariablen hellgrau und die Konsolenvariablen weis dargestellt. Alles was man an den Variablen dann innerhalb des Skriptes ändert passiert an der Kopie, nicht am Original. Wird dann das Skript beendet hat man wieder den Originalzustand, wie vor den Skriptaufruf. Ähnlich ist das mit Funktionen innerhalb eines Skripts. Auch hier erbt die Funktion die Variablen von der Konsole, bzw. des Skriptes. Auch hier wird automatisch wird eine Kopie unter demselben Namen erzeugt. Diese Kopie ist etwas dunkler dargestellt. Alle Änderungen bleiben somit innerhalb der Funktion. Sobald die Funktion beendet ist, gelten wieder die Werte aus dem Skript und ist auch das Skript zu Ende, wieder die Werte der Konsole.

Auch aus Funktionen kann man andere Funktionen aufrufen. Das hat natürlich wieder zur Folge, dass die aufrufende Funktion Ihre Variablen an die Unterfunktion vererbt, aber Änderungen nicht wieder an die aufrufende Funktion zurückgegeben werden. Wie Sie Werte an aufrufende Bereiche zurückgeben können haben Sie im vorangegangenen Abschnitt lesen können. Es gibt allerdings eine Möglichkeit direkt Variablen in einem bestimmten Bereich zu ändern.

Möchten Sie aus einem Skript heraus eine globale Variable (eine Variable von der PowerShell Konsole) ändern, können Sie das indem Sie zwischen dem $ Zeichen und dem Namen der Variablen den Scope-Bezeichner Global einsetzen. Um dies zu verdeutlichen ändern Sie Ihr kleines Skript von eben auf folgenden Inhalt:

$Inhalt

$Inhalt=“Skript

$Inhalt

$Global:Inhalt=“Mit Scope Bezeichner

$Inhalt

Mit dem Scope-Bezeichner Global in der vorletzten Zeile wurde die globale Variable $Inhalt auf der Konsole verändert, nicht die Variable $Inhalt im Skript. Die letzte Zeile enthielt immer noch den Wert Skript. Wenn Sie nun aber auf der Konsole $Inhalt ausgeben lassen werden Sie feststellen, dass nun Mit Scope Bezeichner der Inhalt ist.

Ähnlich ist das innerhalb von Funktionen. Auch hier können Sie gezielt auf den Bereich der Konsole, oder des Skriptes, statt auf den Bereich der Funktion zugreifen. Auf den globalen Bereich funktioniert das wieder genauso wie im vorangegangenen Beispiel. Um auf den Skriptbereich zuzugreifen verwenden Sie statt Global das Wort Skript.

Abgesehen davon, können Sie auch relativ zur eigenen Position, auf andere Bereiche mithilfe einer Nummerierung zugreifen. Mit $0:Variablenbezeichner greifen Sie explizit auf den aktuellen Bereich zu. Mit $1:Variablenbezeichner auf den darüber liegenden. Mit $2:Variablenbezeichner noch einen darüber, usw…

Stellen Sie sich vor, Sie haben ein Skript geschrieben und befinden sich in einer Funktion, die von einer anderen Funktion aufgerufen wurde. Dann würde Sie mit $1:Variablenbezeichner auf die Variablen der aufrufenden Funktion zurückgreifen und mit $2:Variablenbezeichner auf die Skriptvariablen und mit $3:Variablenbezeichner letztendlich auf die PowerShell-Konsole.

Ähnlich wie mit den Variablen verhält es sich mit Funktion, wie im nächsten Abschnitt beschrieben.

2.4.10.2.5            Eigene Cmdlets und Funktionen erstellen

Wenn Sie eine Funktion schreiben, ist diese Funktion zunächst einmal nur innerhalb Ihres Skriptes bekannt.

Unter SuSE-Linux gibt es ein Kommando namens ll, was letztendlich ein Alias auf ls –l darstellt und Dateien mit all Ihren Informationen wiedergibt. In der PowerShell können wir aber keine Aliase mit Parametern versehen, sondern einfach nur von einem Begriff auf ein Cmdlet deuten. Für zusätzliche Parameter müssen wir Funktionen einsetzen. Schreiben Sie folgendes kleines Skript und speichern Sie es ab:

Function ll {

 ls | select *

}

ll

Wenn Sie es starten listet es Ihnen schon die Dateien aus dem aktuellen Verzeichnis auf. Aber was passiert, wenn Sie ll auf der Konsole tippen? Eine Fehlermeldung wird angezeigt, da die Funktion ll nur innerhalb des Skriptes bekannt ist. Ändern Sie die erste Zeile des Skripts wie folgt ab:

Function Global:ll {

Löschen Sie noch die letzte Zeile und speichern Sie es erneut ab. Starten Sie Ihr Skript. Hmm…funktioniert nicht mehr?! Zumindest scheint das Skript nichts gemacht zu haben. Aber nun tippen Sie doch einmal auf der Konsole den „Befehl“ ll ein. Soeben haben Sie Ihren ersten eigenen PowerShell-Befehl geschrieben. J

Durch die Angabe des Bereichs Global wurde Ihre Funktion auf der Konsole bekannt gemacht. Wenn Sie die Konsole schließen und neu starten ist Ihr Befehl allerdings wieder vergessen. Kein Problem! Wenn Sie ihn wieder haben möchten, starten Sie einfach noch einmal Ihr Skript. Ihr Skript ist natürlich nicht auf eine einzelne Funktion beschränkt. Sie können dort gerne mehrere globale Funktionen einfach hintereinander weg schreiben. Lassen Sie dann das Skript ablaufen, werden alle als Global deklarierten Funktionen als Befehle in Ihre Konsole eingebaut. Wenn Sie keine Lust haben jedes Mal das Skript von Hand zu starten, können Sie entweder den Aufruf Ihres Skriptes in die Profildateien hinterlegen, oder die globalen Funktionen direkt in die Profile-Skripte hineinschreiben. Mehr zu Profil Skripten erfahren Sie im Abschnitt über Profil Skripte.

Weitere Informationen und Beispiele über Scopes finden Sie in der PowerShell-Hilfe:

Help about_Scopes

2.4.10.2.6            Parameter by Value

Über die Pipeline können, wie schon mehrfach erwähnt, Objekte übergeben werden. Hier schauen wir uns nun genauer an was dabei passiert.

Kommen Objekte über die Pipeline können diese Objekte ganz unterschiedlicher Art (String, Int, Active-Directory User, u.v.a.m.) sein.

Wenn Sie sich nun einmal die vollständige Hilfe von Start-Service (help sasv -full) anzeigen lassen, schauen Sie sich den Syntax Abschnitt einmal genauer an. Dort gibt es 3 Varianten den Befehl aufzurufen:

-Name
-Displayname

-InputObject

Dies sind die ersten Schalter, die nach dem Bezeichner des Cmdlet folgen. Name und Displayname sind beide vom Datentyp String, während InputObject vom Typ ServiceController ist.

Führen Sie beispielsweise folgende Befehlskette aus:

Get-Service winrm | Start-Service

Was glauben Sie wohl welche Variante hat PowerShell benutzt? Get-Service winrm hat sich ein Object vom Typ ServiceController des Dienstes winrm geholt. Es wird also ein ServiceController Object über die Pipe übergeben, an das Cmdlet Start-Service. Weil der übergebene Value ein ServiceController Object ist, wird also die 3. Variate mit –InputObject ausgeführt.

Warum das wichtig ist? Nun, in diesem Fall unterscheiden sich die restlichen Parameter nicht, aber schauen Sie sich z.B. einmal die Hilfe von Get-WMIObject an. Da werden Sie feststellen, dass dort teilweise ganz unterschiedliche Schalter verwendet werden können.

2.4.10.2.7            Parameter by Name

PowerShell versucht zuerst immer eine Zuordnung der Parameter by Value. Nur wenn dies nicht passt wird die zweite Variante des Zuordnens mittels des Names probiert. Beispiel:

Get-Service | Get-Process 2> $null

Nun, Get-Process, hat so rein gar nichts mit Services zu tun. Wenn Sie mit Get-Help Get-Process -full mal schauen, werden Sie sehen, dass keiner der Parameter von Get-Process ein Objekt vom Typ ServiceController annimmt (was durch Get-Service geliefert wird). Daher wird hier die Zuordnung des ServiceController Objekts mittels Parameter by Value nicht funktionieren. Dafür gibt es aber jede Menge Parameter vom Typ String. Bei den String Parametern ist nun die Frage, an welchen der Parameter die ServiceController Objekte nun zugewiesen werden sollen? Die gelieferten Get-Service Objekte besitzen eine Eigenschaft Name. Get-Process hat einen Parameter mit der Bezeichnung -Name. Da dies nun eindeutig zugewiesen werden kann, erfolg hier die Übergabe der Parameter by Name. Die Eigenschaft des Objekts muss also mit dem Parameter des nachfolgenden Cmdlets übereinstimmen und das nachfolgende Cmdlet muss auch die Zuordnung (in der Hilfe mit dem Schalter –full zu erkennen) über die Pipeline in dieser Form annehmen:

Das oben angegebene Beispiel zeigt Ihnen dann alle „Nicht“-Windows-Dienste an. Haben Sie keine, sondern nur Windows-Dienste, erfolgt einfach keine Ausgabe. Da die Windows-Dienste-Namen nicht mit den Prozessnamen übereinstimmen, liefert Get-Process für die nicht als Prozess gelisteten Namen Fehlermeldungen, die aber durch die Umleitung 2> $null unterdrückt werden. Auf den meisten PCs findet sich aber min. 1 zusätzlicher Dienst.

2.4.10.2.8            Parameterübergabe im Detail

Wenn Sie im Team programmieren, ist es gut zu wissen, was sich die Kollegen bei bestimmten Funktionen die Sie geschrieben haben, dachten. Oder auch wenn Sie als PowerShell Guru für normal sterbliche Administratoren und Anwender PowerShell Befehle schreiben, soll man gleich erkennen wie Ihre Kommandos aufgebaut sind und funktionieren. Abgesehen davon ist es auch schön, wenn die PowerShell falsche Eingaben von sich aus gleich ablehnt und Sie sich nicht selbst um fehlerhafte Ausführung Ihrer Kommandos kümmern müssen. Je genauer Sie Ihre Parameter deklarieren, umso mehr Ärger nimmt Ihnen die PowerShell durch Standardfehlermeldungen ab.

Die PowerShell in der Version 1.0 hat keinen Befehl um Webseiten einzulesen, dass .NET-Framework schon. In Version 2.0 gibt es zwar mit dem Modul BitsTransfer ganz nette Cmdlets für Webtransfers, aber dazu muss auch Server seitig Bits (Bits=Backgroud Intelligent Transfer Service) unterstützt werden. Hier soll auch einfach nur verdeutlicht werden, wie Sie ordentlich Parameter übergeben und dafür bauen Sie sich doch am besten ein Cmdlet um Webseiten in der PowerShell einzulesen.

Nach kurzer Internetrecherche dürften Sie auf die .NET-Klasse System.Net.Webclient gestoßen sein. Davon sollten Sie sich zunächst einmal ein Objekt bauen:

$Webclient=New-Object System.Net.WebClient

Ein Get-Member verrät uns, dass es hier u. a. eine Methode DownloadString gibt, welche eine klassische Webpfadangabe (URI=Uniform Ressource Indicator oftmals auch unter der Bezeichnung URL=Uniform Ressource Locator) in der Form: http://www.irgendwo.im.internet.de erwartet. Holen Sie sich doch zum Testen einfach einmal meine Internetseite, oder irgendeine Ihrer Wahl ab:

$Webclient.DownloadString(“http://www.martinlehmann.de/wp“)

Gar nicht so schwer, wenn man es erst einmal gefunden hat ;-). Jetzt wollen wir daraus einmal einen PowerShell Cmdlet namens Get-Website basteln:

Function Global:Get-Website {

 Param(

  [String]$Website

 )

 $Webclient=New-Object System.Net.WebClient

 $Webclient.DownloadString($Website)

}

Speichern Sie den Code als Skript ab und starten Sie das Skript. Danach tippen Sie:

Get-Website http://www.martinlehmann.de/wp

Vielleicht haben Sie gemerkt, dass Ihr eigenes Cmdlet bereits voll in die PowerShell integriert ist, wenn Sie versucht haben mit Tab-Vervollständigung zu arbeiten. Get-w + Tab-Taste vervollständigt nämlich schon Ihr Cmdlet und Sie brauchen nur noch die Website einzugeben, die Sie abholen möchten. Aber das ist noch nicht alles! Geben Sie doch einmal Help Get-Website ein. Dann stellen Sie fest, dass schon eine rudimentäre Hilfe zu Ihrem Cmdlet angegeben wird. Man erhält die Information, dass es einen Schalter –Website gibt und dieser einen String erwartet. Das liegt an Ihrer Parameter Definition. Der Name der Variablen wird automatisch zum Schalter und weil Sie die Variable gleich als String deklariert haben, wird auch diese Information in der Hilfe angezeigt. Geben Sie doch einmal nur Get-Website ohne Parameter ein. Das führt zu einer unschönen Fehlermeldung. Es gibt verschiedene Möglichkeiten diesem Problem zu begegnen.

Erstens: Sie weisen einen Standardwert zu, der verwendet wird, wenn kein Parameter übergeben wird. Dazu tauschen Sie einfach im Parameterblock, die Zeile mit der Variablen wie folgt aus:

  [String]$Website=“http://www.martinlehmann.de/wp“

Speichern Sie das Skript und starten Sie es erneut.

Wird nun nur das Cmdlet eingetippt, wird eben immer meine Website aufgerufen. Gibt man jedoch eine Internetseite vor, hat dies Vorrang vor dem was im Parameterblock zugewiesen wird. Auf diese Weise lassen sich also Standardwerte zuweisen.

Zweitens: Fordern Sie den Benutzer auf eine Eingabe zu machen. Auch hier tauschen Sie bitte einfach wieder die Variablendefinition im Parameterblock aus:

  [String]$Website=(Read-Host -Prompt "Bitte geben Sie eine Internetadresse ein")

Speichern Sie die Änderung, führen Sie das Skript erneut aus und versuchen Sie Ihr Cmdlet ohne Angabe der URI zu starten. Wenn Sie mögen können Sie dem Benutzer auch bei Beispiel mit angeben.

…und zum Dritten mit folgendem Parameterblock:

Param(

  [Parameter(Mandatory=$true)]

  [String]$Website

 )

Durch die zusätzliche Parameter-Beschreibung Mandatory=$true weiß die PowerShell, dass die nachfolgende Variable eingegeben werden muss. Fehlt die Angabe, fordert PowerShell mit einen Standardtext zur Eingabe des fehlenden Parameters auf. Das ist nicht so Informativ, wie die Sache mit Read-Host, dafür aber sprachneutral.

Für den [Parameter()] Abschnitt können noch mehr Angaben gemacht werden, wie z.B.:

 [Parameter(ValueFromPipeline=$true,Position=0,HelpMessage=“HilfeInfo“)]

ValueFromPipeline sagt aus, ob Eingaben von einer Pipeline an diese Variable übergeben werden können. In unserem Beispiel also z.B. so:

“http://www.martinlehmann.de/wp“ | Get-Website

Position ist bei nur einem einzelnen Parameter nicht wirklich spannend, aber bei mehreren Parametern können Sie angeben in welcher Reihenfolge die Angaben an die jeweiligen Variablen geschickt werden.

HelpMessage kann einen zusätzlichen Infotext anzeigen, wenn der Benutzer den Parameter nicht angegeben hat. Wenn der Parameter eingegeben werden muss (Mandatory=$true) erscheint folgende Meldung in der PowerShell:

Cmdlet Get-Website an der Befehlspipelineposition 1
Geben Sie Werte für die folgenden Parameter an:
(Geben Sie !? ein, um Hilfe zu erhalten.)

Wenn der Benutzer dann !? und Enter tippt, bekommt er den Text aus HelpMessage angezeigt.

Wie Sie wissen, müssen Parameter nur so weit eingetippt werden bis sie eindeutig sind. Vielleicht möchten Sie aber auch einen speziellen Kurznamen für einen Schalter definieren. Wenn Sie z.B. einen Schalter Namens –Benutzername haben, könnten Sie eine Kurzform wie –BN einbauen:

Param(

    [Parameter(Mandatory=$true)]

    [Alias("BN")]

    [String]

    $BenutzerName

)

Wichtig dabei ist zu erkennen, dass die Aliasdefinition in einer eigenen Zeile steht und nicht in Parameterzeile, wie die vorangegangenen Beispiele. Noch mehr Möglichkeiten für die Parameterdefinition finden Sie in help about_Functions_Advanced_Parameters.

Hier noch eine kurze Beschreibung weiterer Möglichkeiten:

[AllowNull()]

Erlaubt explizit, dass kein Wert übergeben wird.

[ValidateNotNull()]

Verbietet explizit, dass kein Wert übergeben wird.

[AllowEmptyString()]

Läßt explizit die Übergabe einer leeren Zeichenkette zu.

[AllowEmptyCollection()]

Läßt explizit die Übergabe eines Arrays ohne Objekte zu.

[ValidateNotNullOrEmpty()]

Es darf weder eine leere Zeichenkette, noch gar nichts übergeben werden. Es muss also auf jeden Fall irgendein Wert übergeben werden.

[ValidateCount(x,y)]

Legt fest, dass min. x, aber max. y Werte übergeben werden.

[ValidateLength(x,y)]

Legt fest, dass der übergebene Wert min. x, aber max. y Zeichen lang sein darf.

[ValidatePattern(RegulärerAusdruck)]

Mithilfe regulärer Ausdrücke festlegen, was übergeben werden darf.

[ValidateRange(x,y)]

Übergebene Zahlen dürfen nur im Bereich zwischen x und y liegen.

[ValidateSet(x,y,z,…)]

Funktioniert ähnlich wie ein Schalter, nur die Werte x,y,z,usw. werden akzeptiert.

ValidateScript({})]

Erfordert die Übergabe eines Scripts. Nur wenn das Script erfolgreich abläuft, wird die Funktion ausgeführt.

 

2.4.10.2.9            Ergänzen der allgemeinen Schalter (Common Parameters)

Standardmäßig unterstützt Ihr Script keine CommonParameters (also ‑Verbose, ‑Debug, ‑ErrorAction usw.). Um diese ebenfalls bereit zu stellen, können Sie ganz einfach den Begriff [CmdletBinding()] Ihrer Funktion hinzufügen. Die Funktionalität wird automatisch über PowerShell bereitgestellt.

Das können Sie aber ganz einfach implementieren:

 

Function Name {

 [CmdletBinding()]

 Param (

  $VariableFürWert 1,

  $VariableFürWert 2,

  usw.

 )

 Ihr ScriptCode

}

Setzen Sie dann in Ihrem Skript beispielsweise die Zeile:

Write-Verbose "Detail Infos"

…würde den Text "Detail Infos" nur dann ausgeben, wenn die Funktion mit dem Schalter ‑Verbose aufgerufen wird. Ähnlich verhält sich das Cmdlet Write-Debug, wenn die Funktion mit dem Schalter –Debug aufgerufen wird. Allerdings wird das Skript an dieser Stelle auch angehalten und erst der Benutzer gefragt, ob es weiter gehen soll.

Um –Confirm oder –WhatIf zu unterstützen müssen Sie zusätzlich in [CmdletBinding()] SupportsShouldProcess=$true einsetzen:

[CmdletBinding(SupportsShouldProcess=$true)]

Des Weiteren müssen Sie eine Wenn-Dann-Abfragen mit Hilfe der $PSCmdlet Variable in Ihr Script einbauen:

If ($PSCmdlet.ShouldProcess(”Deleting File $DateiDieDurchIhrScriptGelöschtWird”)) {remove $DateiDieDurchIhrScriptGelöschtWird}

Würde das Script mit dem Schalter –WhatIf ausgeführt, wird die Löschanforderung in der geschweiften Klammer nicht ausgeführt, sondern nur der Text:

WhatIf: Ausführen des Vorgangs NameIhrerFunktion für das Ziel "Deleting File DateiDieDurchIhrScriptGelöschtWird".

angezeigt. Bei –Confirm würde der Benutzer gefragt werden, ob die Aktion in den geschweiften Klammern ausgeführt werden soll.

2.4.10.2.10        Praxisbeispiel: Arrays miteinander vergleichen

Wie im Abschnitt Besondere Variablentypen:Array versprochen, wollen wir hier nun dem Problem Arrayvergleich auf den Grund gehen.

Tippen Sie doch einmal:

1 –eq 1

ein. Da sagt die PowerShell True. Bei

2 –eq 1

sagt Sie False. So soll das sein! Dann bauen Sie sich doch einmal kurz folgende 3 Arrays:

$master="Eins","Zwei","Drei"

$vergleich1=$master

$vergleich2="Ein","Zwo","Drei"

$master soll Ihnen hier als Vergleichsvorlage dienen. $vergleich1 bekommt denselben Inhalt wie $master. $vergleich2 stimmt nur im letzten Element mit $master überein, die ersten beiden unterscheiden sich.

Nun versuchen Sie einmal zu vergleichen:

$master -eq $vergleich1

$master -eq $vergleich2

Was sagt die PowerShell dazu? Gar nichts! Lassen Sie uns eine Funktion dafür bauen:

10.         Function Global:Compare-Array {

11.          Param(

12.           [Array]$Array1,

13.           [Array]$Array2

14.          )

15.          If ($Array1.count –eq $Array2.count) {

16.           $Gleich=$true

17.           For ($Cnt=0;$Cnt –lt $Array1.count;$Cnt++) {

18.            If ($Array1[$Cnt] –ne $Array2[$Cnt]) {$Gleich=$false}

19.           }

20.           If ($Gleich) {$true} else {$false}

21.          } else {

22.           $false

23.          }

24.         }

Der Param Block sorgt dafür, dass Sie genau zwei Arrays übergeben bekommen. Danach folgt in Zeile 15 zunächst einmal ein Vergleich der Anzahl der Elemente in den beiden Arrays. Wenn Sie unterschiedlich viele Elemente haben, können Sie schon einmal nicht gleich sein, dementsprechend wird in 22. Zeile nach der else Anweisung einfach nur $false zurückgegeben.  In Zeile 16 gehen wir zunächst einmal davon aus, dass alles passt und setzen die Variable $Gleich auf $true. Dann folgt in Zeile 17 eine Schleife mit einem Zähler über alle Elemente. Bei 3 Elementen wird also innerhalb der For Schleife in der Variablen $Cnt von 0 bis 2 gezählt. In Zeile 18 liegt die Magie. Hier vergleichen Sie das jeweilige Element aus $Array1 mit dem dazugehörigen Element aus $Array2. Wenn das bei einem der Schleifen Durchläufe nicht passt wird die Variable $Gleich auf $false gesetzt. Nachdem alle Schleifendurchläufe abgearbeitet sind und somit alle Elemente miteinander verglichen, testen Sie in Zeile 20, ob $Gleich noch $true ist. Wenn ja, wird $true zurück geliefert, wenn nein muss min. ein Eintrag die Variable $Gleich geändert haben und somit wird $false zurück geliefert.

Compare-Array $master $vergleich1

Liefert wie erwartet True.

Compare-Array $master $vergleich2

Liefert wie erwartet False.

Compare-Array $master "eins","zwei"

Liefert ebenfalls False, weil schon die Anzahl der Elemente nicht stimmt.

Keine Ahnung warum es nicht standardmäßig so ein Cmdlet gibt. Aber so habe ich auch etwas zu berichten ;-). Und es stellt noch eine tolle Zusammenfassung des bisher Erlernten dar. Die Funktion ist bei weitem noch nicht perfekt. So könnten Sie z.B. die Arrays erst einmal sortieren lassen. Oder sobald ein $false beim Vergleich der einzelnen Elemente auftritt, könnten Sie sich den Rest der Schleife sparen und die Schleife vorzeitig abbrechen, was die Funktion auch gleich noch schneller macht. Na, wissen Sie noch wie das geht?

2.4.11                     Profil Skripte

Hier erfahren Sie, wie Sie Ihre PowerShell Konsole dauerhaft anpassen können und auch um eigene Befehle erweitern können.

In ein Profil Skript können Sie all das hineinschreiben, was Sie auch in andere Skripte hineinschreiben, oder direkt an der Shell als Befehl absetzen. Besonders gut geeignet sind bestimmte Variablen- und Aliasdefinition die nicht standardmäßig vorhanden sind, die Sie aber gerne immer in Ihrer Kommandozeile zur Verfügung hätten. Aber nicht nur dass, sondern auch ganze Funktionen (eigene PowerShell Cmdlets) können Sie in einem Profil Skript verewigen.

Ein System weit gültiges Profil Skript muss im Verzeichnis C:\Windows\System32\WindowsPowerShell\v1.0 liegen und den Namen Profile.ps1 tragen.

Dieses Skript wird automatisch jedes Mal ausgeführt, wenn Sie einen PowerShell Prozess starten.

Hinweis: Wenn Sie Funktionen in Profil Skripten verwenden, entfällt die Anweisung Global:, da PowerShell davon ausgeht, dass hier stehende Funktionen auf jeden Fall als Befehl auf der Konsole zur Verfügung stehen sollen. Wenn Sie möchten, dass ein Funktion nicht von der Konsole als Befehl genutzt werden kann müssen Sie statt des Global: wie in normalen Skripten, die Bezeichnung Script: vor den Funktionsnamen schreiben.

2.4.11.1                Ablageort bei verschiedenen Betriebssystem-Versionen

Statt für den gesamten PC ein Profil Skript anzulegen, können Sie das auch gerne für bestimmte Benutzer tun. Also persönliche Profil Skripte sind auch machbar. Wenn Sie mögen können Sie auch beide Varianten kombinieren. Wobei immer zuerst das Systemweite Profil Skript ausgeführt wird und danach das Skript im Benutzerprofil. Sind in beiden widersprüchliche Angaben vorhanden, gelten immer die des Benutzers, da es zeitlich danach ausgeführt wird.

Bei XP und Windows Server 2003 muss das persönliche Profile.ps1 Skript unter:

C:\Dokumente und Einstellungen\Name des Benutzers\Eigene Dateien\WindowsPowerShell

liegen. Bei Vista, Server 2008 und neueren Betriebssystemen unter:

C:\Benutzer\Name des Benutzers\Dokumente\WindowsPowerShell

In der automatischen Variable $Profile sind die Pfade hinterlegt. Mit

$Profile | Select *

können Sie sich alle Pfade anzeigen lassen. Theoretisch könnten Sie die auch verändern, aber davon würde ich abraten. Das führt nur zu Verwirrungen.

Wenn Sie statt Profile.ps1 den Dateinamen Microsoft.PowerShellISE_profile.ps1 verwenden wir das Profil nur in der ISE ausgeführt.

Auf Linux Systemen ist der Pfad für alle Benutzer:

$PsHome\Profile.ps1

und für einen selbst:

$Home\My Documents\PowerShell\profile.ps1

2.4.11.2                .NET-Objekte dauerhaft erweitern

In den vorangegangenen Kapiteln haben Sie viel über Objekte und Skripting gelernt. Wenn Sie gerne dauerhaft Änderungen an .NET-Objekten machen möchten, sodass diese Ihnen beim Start jeder PowerShell zur Verfügung stehen, müssen Sie das etwas umständlich mithilfe der Profile-Skripte in XML-Dateien verpackt durchführen.

2.4.11.2.1            Eigenschaften .NET Objekten dauerhaft hinzufügen

Hatten Sie schon einmal das Problem, dass ein Prozess sich nicht beenden läßt? Nun, wenn Sie einen störrischen Prozess haben, nieten Sie doch die Eltern um. Das macht den Prozess so traurig, dass auch er Selbstmord begeht. Klingt sehr barbarisch, aber funktioniert. ;-)

Dazu muss man die Eltern aber erst einmal ausfindig machen und dabei ist Get-Process leider nicht besonders hilfreich. Doch dass können Sie ändern!

Erstellen Sie eine Datei namens ppid.ps1xml mit folgendem Inhalt:

10.         <Types>

11.          <Type>

12.           <Name>System.Diagnostics.Process</Name>

13.           <Members>

14.            <ScriptProperty>

15.             <Name>ppid</Name>

16.             <GetScriptBlock>

17.              (Get-WmiObject win32_process | Where-Object {$_.processid –eq $this.id} | select parentprocessid).parentprocessid

18.             </GetScriptBlock>

19.            </ScriptProperty>

20.           </Members>

21.          </Type>

22.         </Types>

Wegen der ordentlichen Darstellung beginne ich hier bei 10. zu zählen. Die Nummer, wie üblich, bitte nicht mit abtippen, da Sie nur zur Orientierung dienen sollen. Alle nicht hervorgehobenen Zeilen müssen immer so aussehen. Spannend wird es ab Zeile 12. Hier steht System.Diagnostic.Process. Wo bekommen Sie diese Information her?  Nun, das ist die Objektklasse an der wir rumschrauben wollen. Wenn Sie Get-Process | Get-Member ausführen, sehen Sie ganz oben den TypeName: System.Diagnostic.Process und bei der Gelegenheit können Sie auch gleich mal alle Properties nach PPID absuchen. Gibt’s nicht, richtig?

In Zeile 14 sagen Sie durch ScriptProperty aus, dass Sie hier eine Eigenschaft (keine Methode) hinzufügen möchten.

Mit Zeile 15 legen Sie den Namen für Ihre „selbst gestrickte“ Eigenschaft fest.

Zeile 16 ist spezifisch für Properties GetScriptBlock anzugeben.

In Zeile 17 liegt Ihr PowerShell Skript Code, der mithilfe von WMI die ParentProcessID (den Elternprozess) ausfindig macht. Wie das mit dem WMI grundsätzlich funktioniert, ist im Abschnitt WMI Objekte erklärt. Die Besonderheit hier, liegt in der Variablen $this bzw. $this.id. Wenn Sie Get‑Process ausführen, bekommen Sie einen Array von Prozess-Objekten. Jedes dieser Objekte landet bei dieser XML-Erweiterung zur Laufzeit in der Variablen $this. Das funktioniert also so ähnlich wie $_ mit der Pipe. Wenn Sie noch einmal bei Get-Process | Get-Member in Ausgabe reinschauen, werden Sie feststellen, dass es da eine Eigenschaft namens id gibt. Wenn Sie nach einer Pipe auf die Eigenschaft z.B. bei Verwendung von Where-Object auf diese Eigenschaft zugreifen wollten, würden Sie $_.id schreiben. Ersetzen Sie also hier an dieser Stelle einfach $_.id durch this.id. Damit haben Sie beim späteren Aufruf von Get-Process die Prozess Nummer. Sie suchen hier einfach in allen laufenden Prozessen mittels WMI Abfrage nach dem Prozess mit der Prozessnummer des jeweiligen Objekts. Das WMI-Objekt Win32_Process liefert im Gegensatz zu Get-Process aber nicht nur die id als Eigenschaft, sondern auch den parentprocessid (den Elternprozess). Die picken Sie sich nun mit select aus den ganzen Eigenschaften heraus und hinterlegen das Ergebnis automatisch in der Eigenschaft namens ppid (aus Zeile 15).

Die restlichen Zeilen schließen nur noch die zuvor geöffneten Tags (Markierungen im XML Dokument).

Jetzt müssen Sie der PowerShell nur noch verklickern, dass Sie diese Datei benutzen soll. Das ist nun wieder etwas einfacher:

update-typedata ppid.ps1xml

bzw. syntaktisch, wenn die ppid.ps1xml nicht im aktuellen Verzeichnis liegt:

update-typedata Laufwerk:\Pfad zur Datei\ppid.ps1xml

Wenn Sie nach Eingabe von update-typedata (vorausgesetzt der Befehl wurde ohne Fehlermeldung ausgeführt. Wenn nicht, suchen Sie nach Tippfehlern in Ihrer ppid.ps1xml) nun Get-Process | Get-Member aufrufen werden Sie Ihre neue Eigenschaft finden. Die können Sie nun wie jede andere Eigenschaft auch z.B. mit Select oder Where-Object auswerten.

Haben Sie schon eine Idee, wie Sie das nun permanent hinterlegen? Schreiben Sie die Anweisung update-typedata einfach in eines Ihrer Profile-Skripte! Schon habe Sie bei jeder PowerShell Konsole die Sie öffnen in Ihrem Get-Process eine echt coole neue Eigenschaft.

Durch Komma getrennt können Sie auch mehrere Erweitungsdateien auf einmal angeben, wie z.B. die Datei aus dem nachfolgenden Beispiel mit der Methoden Erweiterung.

2.4.11.2.2            Methode .NET Objekten dauerhaft hinzufügen

Um eine Text basierte Datei z.B. mit notepad zu öffnen ist viel Tipparbeit nötig. Z.B.:

ls TextDatei.txt | foreach {notepad $_.fullname}

Trotzdem es nur eine einzelne Datei ist, welche über die Pipe kommt, müssen Sie, damit es funktioniert, ein foreach drum herum wickeln. Wenn Sie mit Get-Member mal schauen, werden Sie Methoden wie Open, OpenRead oder gar OpenText finden. Nur leider klappen die irgendwie nicht, zumindest nicht mit der gewünschten Anwendung notepad. Wenn Sie nun bestimmt Dinge mit Dateien machen möchten, wie in diesem einfachen Fall „mit Notepad öffnen“, dann wäre es doch schön eine Methode zu haben, die man einfach angibt und schon passiert der Rest wie von Geisterhand.

Dazu legen Sie sich zunächst eine Datei namens openedit.ps1xml mit folgendem Inhalt an:

10.         <Types>

11.          <Type>

12.           <Name>System.IO.FileInfo</Name>

13.           <Members>

14.            <ScriptMethod>

15.             <Name>OpenEdit</Name>

16.             <Script>

17.              if ($this -match "\.(log|txt|dat|reg|csv|ps1|bat|cmd)$"){

18.               Notepad.exe $this.Fullname

19.              } else {

20.               write-host "Sorry, aber das Dateiformat von $this wird nicht unterstützt!`nNur: log, txt, dat, reg, ps1, bat, cmd oder csv" -foregroundcolor red

21.              }

22.             </Script>

23.            </ScriptMethod>     

24.           </Members>

25.          </Type>

26.         </Types>

Der grundlegende Aufbau ist wieder ähnlich wie bei der Ergänzung einer Eigenschaft.

Zeile 12 kommt dieses Mal von der Objektklasse für Dateien. Wissen Sie noch wo Sie die Info herbekommen (schauen Sie ggf. im vorangegangenen Abschnitt nach)?

In Zeile 14 steht nun ScriptMethod statt ScriptProperty und in Zeile 16 einfach nur Script statt GetScriptBlock.

Zeile 17-20 enthält die Funktion, die angewandt werden soll. Hier ist nun eine kleine Fehlerprüfung eingebaut, damit die Zeile 18 nur dann ausgeführt wird, wenn die Datei mit einer der in Zeile 17 angegebenen Dateinamenerweiterungen endet. Damit man die Methode nicht versehentlich auf Binärdateien oder Word-Dokumente anwedet. Das könnte hässlich werden. Zeile 18 startet nun Notepad mit der entsprechenden Datei. Die Eigenschaft Fullname der Datei enthält den kompletten Pfad zur Datei. Falls die Dateinamenerweiterung nicht passt, wird die Fehlermeldung in Zeile 20 ausgegeben.

Zeile 21 schließt die if-else Anweisung und gehört daher noch zu Ihrem ausführbaren PowerShell Code.

Der Rest schließt wieder nur die offenen Tags.

Jetzt noch ein update-typedata openedit.ps1xml und Ihre Dateiobjekte haben eine neue Methode namens Openedit.

Nun können Sie ganz einfach:

(ls TextDatei.txt).openedit()

schreiben und schon öffnet sich die Textdatei.txt mit Notepad.

Dauerhaft? Packen Sie den update-typedata in eines Ihrer Profile-Skripte. Gerne auch zusammen mit der Eigenschaftsänderung z.B. so:

update-typedata ppid.ps1xml,openedit.ps1xml

Selbstverständlich können Sie auch mehrere Erweiterungen in einer einzelnen Datei zusammenfassen.

2.4.11.3                Dot-Sourcing, Skripte in andere Skripte einbinden

Profil Skripte können schnell unübersichtlich werden. Ein weiteres Problem stellt die Aktualisierung dar. Wenn alle PCs der Firma, oder zumindest mehrere Maschinen dieselben Standardvorgaben haben sollen wäre es ziemlich aufwändig auf allen PCs die Profil Skripte zu aktualisieren. Sie haben die Möglichkeit von einem Skript ein anderes Skript aufzurufen. Nachdem Sie den Abschnitt über Scopes durchgearbeitet haben, liegt das Problem aber auf der Hand. Die Funktionen (z.B. eigene PowerShell Cmdlets) von anderen Skripts sind im aufrufenden Skript unbekannt. Hier hilft die Methode des Dot-Sourcings. Hört sich kompliziert an, ist aber ganz einfach. Dazu schreiben Sie lediglich noch einen Punkt und ein Leerzeichen vor den Aufruf des einzubindenden Skripts. Ein ordentlich, aufgeräumtes Profil Skript könnte z.B. so aussehen:

# Mein persönliches Profile-Skript

# Meine Alias-Definitionen

. ./MeineAliase.ps1

# Meine Active-Directory Cmdlets

. ./ADCmdlets.ps1

# Meine WMI-basierten Cmdlets

. ./WMICmdlets.ps1

# Ein paar weitere allgemeine Einstellungen

Um ein zentrales Skript-Repository zu erstellen, könnten Sie auf einem Dateiserver eine Freigabe anlegen und dort Ihre gesamten Skripte hinterlegen. Um nun von PCs aus einfach den zentralen Speicher mit den Skripten einzubauen, brauchen Sie nur eine Zeile in die Profil Skripte Ihrer Clients zu schreiben:

Get-ChildItem \\Servername\Freigabename -Filter *.ps1 -Recurse | Foreach {. $_.FullName}

Somit brauchen Sie nicht mehr alle Stationen von Hand zu aktualisieren. Sobald ein Skript sauber funktioniert, kopieren Sie es in die Freigabe und alle Clients bauen es automatisch ein. Dank des Filters und des Recurse können Sie auch gerne Unterverzeichnisse anlegen, die auch in der Freigabe für Übersicht sorgen. Das nicht jeder dahergelaufene Schreibzugriff auf die Freigabe haben sollte, muss ich wohl nicht extra erwähnen, oder doch?

Übungsaufgabe: Erstellen Sie ein Skript, um die Festplatte nach bestimmten Dateien und deren Inhalt zu durchsuchen. Das Suchskript soll 3 Parameter verarbeiten:

1.       Von welchem Verzeichnis aus soll im Dateisystem gesucht werden (z.B.: c:\Users)?

2.       Suchmuster für den Dateinamen (z.B.: *.ps1).

3.       Suchbegriff nach dem die Dateien durchforstet werden (z.B.: Foreach)

Syntax: Suchskript.ps1 Verzeichnis Dateinamen Inhalt

Einen Lösungsansatz für diese primitive Variante finden Sie im Buchteil Praxisbeispiele im Kapitel Filesystem und dort im Abschnitt Skript um Festplatte nach bestimmten Dateien und deren Inhalt zu durchsuchen.

Zusatzaufgabe 1: Noch besser wäre, wenn das Skript nicht direkt die Aufgabe ausführt, sondern eine Funktion bereit stellt, die wie ein Cmdlet genutzt werden kann.

Zusatzaufgabe 2: Wenn Sie es perfekt machen möchten, rufen Sie das Suchskript durch das Profile-Skript auf, sodass Ihnen auf jeder PowerShell Konsole die Sucherweiterung zur Verfügung steht.

2.4.12                     Skripttuning

Wenn Ihre Skripte größer werden, kann es schon einmal vorkommen, dass der Ablauf zu lange dauert. In diesem Abschnitt geht es darum Ihre Skripte zu beschleunigen und Speicher zu sparen.

2.4.12.1                Wie schnell ist Ihr Skript?

Um festzustellen, ob Ihre Tuningmaßnahmen erfolgreich waren, müssen Sie wissen wie lange Ihr Skript zur Ausführung benötigt hat. Das lässt sich mittels des Cmdlet Measure-Command ganz schnell überprüfen:

Measure-Command {IhrSkript.ps1 oder auch gerne eine Befehlskette}

Da es zu verschiedenen Ausführungszeitpunkten zu unterschiedlichen Messergebnissen kommt, sollten Sie vielleicht mehrere Messungen durchführen und darüber einen Mittelwert bilden:

$Messung1=For ($i=1;$i -le 10;$i++) {Measure-Command {IhrSkript.ps1}}

$Messung1 | Measure-Object –Property TotalMilliseconds –Average

Natürlich können Sie auch über Sekunden oder Minuten den Mittelwert bilden. Von Stunden oder gar Tagen wollen wir hier einmal nicht ausgehen ;-).

2.4.12.2                Alias oder Cmdlet?

Bei meinen eigenen Skripten habe ich festgestellt, dass man nicht grundsätzlich sagen kann, dass die Verwendung eines Alias oder eine Cmdlets schneller abgearbeitet wird. Im Abschnitt über gutes Skripting habe ich geschrieben, dass Sie Cmdlets bevorzugen sollen und reichlich Kommentieren, was Sie da zusammen geschrieben haben. Da Aliase manchmal schneller funktionieren, als Cmdlets muss ich mich in Sachen Verarbeitungsgeschwindigkeit von dieser Aussage leider wieder distanzieren. Probieren Sie Aliase und messen Sie nach! Auch Kommentarzeilen verlangsamen ein Skript spürbar. Eigentlich sollte man meinen dass Kommentarzeilen oder Aliase keinen oder zumindest keinen spürbaren Einfluss auf die Verarbeitungsgeschwindigkeit haben, aber wenn Sie nachmessen, werden Sie erstaunt sein, wie viel das ausmachen kann. Insbesondere wenn das innerhalb von Schleifen geschieht.

2.4.12.3                Foreach vs. ForEach-Object

Siehe Beschreibung ForEach-Object Cmdlet.

2.4.12.4                Where-Object

Where-Object oder kurz ? ist ein Allheilmittel, das immer und überall funktioniert. Vor allem können Sie damit jede X-beliebige Eigenschaft und deren Kombinationen abfragen. Der Nachteil ist allerdings, dass es jedes Mal interpretiert werden muss und das dauert im Vergleich recht lange. Vergleich zu was? Nun, wenn Sie bei einem Cmdlet einen Parameter wie –Filter, -Exclude oder –Include haben, dann sollten Sie der Parameter Variante gegenüber dem Where-Object Cmdlet den Vorzug geben. Um einmal den Unterschied zu verdeutlichen, lassen Sie sich doch einmal alle Textdateien vom Verzeichnis C:\Windows auflisten:

Measure-Command {ls c:\windows -Filter *.txt -Recurse 2> $null}

Measure-Command {ls c:\windows -Recurse 2> $null | ? {$_.name -like "*.txt"}}

Das macht einen ganz schönen Unterschied aus, was? Um Fehlermeldungen für Bereiche auf die der Zugriff verweigert ist zu unterdrücken, wurden diese mittels 2> $null ins Nirwana geschickt.

2.4.12.5                Piping

2.4.12.5.1            Funktionen

Funktionen lassen sich noch etwas beschleunigen. Innerhalb einer Funktion können Sie mit Unterteilungen arbeiten. Werden beispielsweise von einer Pipeline mehrere Werte an Ihre Funktion geliefert, können Sie Ihrer Funktion klar machen, was nur ein einziges Mal zu Beginn der Verarbeitung durchgeführt werden soll. Das schreiben Sie in den Begin {} Abschnitt. Was für jedes Element durchlaufen werden soll schreiben Sie in den Process {} Abschnitt. Was zum Schluss, nach dem letzten Element geschehen soll, schreiben Sie in den End {} Abschnitt:

10  Function NamederFunktion {

11  Begin {

12   Hier könnten Sie z. B. den aktuellen Verzeichnispfad, oder andere Ausgangsbedingungen in eine Variable retten bevor Sie zu wüten anfangen.

13  }

14  Process {

15    Hier findet die eigentlich Verarbeitung statt. Möglicher Weise benutzen Sie cd um Verzeichnisse zu wechseln etc...

16  }

17  End {

18    Wechseln Sie wieder in das ursprüngliche Verzeichnis zurück und geben Sie Variablen frei und führen Sie weitere "Aufräumvorgänge" durch.

19  }

20  }

Als praktisches Beispiel greifen wir einmal auf die Währungskurse der Europäischen Zentralbank zu. Schreiben Sie dazu folgendes langsames Skript:

10  Function Global:Get-Wechselkurs {

11   Param(

12    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]

13    [Double[]]

14    $Betrag,

15    [String]

16    $Waehrung="USD"

17   )

18  # Für jede zu berechnende Zahl den aktuellen Wechselkurs bestimmen

19   $Xml=New-Object xml

20   $Xml.Load('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')

21   $Wechselkurse=$Xml.Envelope.Cube.Cube.Cube

22   $Wert=$Wechelkurse | ? {$_.Currency -eq $Waehrung} | select -expandproperty rate

23  # Betrag ausrechnen

24   $Betrag*$Wert

25  }

Starten Sie das Skript und messen Sie die Zeit, wie lange es dauert für 100 Zahlen den US-Dollar-Wert (mit dem Schalter -Waehrung können Sie auch andere Währungen angeben) zu bestimmen:

Measure-Command {1..100 | Get-Wechselkurs}

Bei der Funktion wird für jede von der Pipeline übergebene Zahl auf das Internet zugegriffen und der aktuelle Wechselkurs abgeholt. Ich hoffe Sie haben nicht gerade eine Mobil oder ISDN Verbindung, dann wird das sehr deutlich. Mit dem folgenden Skript legen Sie fest, dass zu Beginn der Wechselkurs abgeholt wird und dann nur noch die Berechnungen durchgeführt werden:

10  Function Global:Get-Wechselkurs {

11   Param(

12    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]

13    [Double[]]

14    $Betrag,

15    [String]

16    $Waehrung="USD"

17   )

18  Begin {

19  # Einmalig zu Beginn aktuellen Wechselkurs bestimmen

20    $Xml=New-Object xml

21    $Xml.Load('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')

22    $Wechselkurse=$Xml.Envelope.Cube.Cube.Cube

23    $Wert=$Wechelkurse | ? {$_.Currency -like $Waehrung} | select -expandproperty rate

24  }

25  Process {

26  # Betrag ausrechnen

27    $Betrag*$Wert

28  }

29  }

Speichern Sie das Skript und rufen Sie es auf, danach messen Sie zum Vergleich wieder die Verarbeitungszeit:

Measure-Command {1..100 | Get-Wechselkurs}

Der Begin Abschnitt zur Beschaffung des Wechselkurses wird nur ein einziges Mal vor der Berechnung der ersten Zahl ausgeführt. Nur der Process Abschnitt wird für die Zahlen 1 bis 100 durchlaufen. So wie in diesem Beispiel der End Abschnitt entfällt, kann natürlich auch der Begin Abschnitt weggelassen werden.

Achtung ! Das der Process-Abschnitt für jeden Wert automatisch durchlaufen wird klappt nur, wenn die Werte über die Pipeline kommen. Gibt man mehrere Werte hinter dem Schalter –Betrag an, kann das bei mehrzeiligen Process-Blöcken schief gehen. In dem Fall muss man einfach eine Foreach-Schleife (bzw. Foreach-Object – siehe Abschnitt Foreach-Object) in den Process-Block einfügen wie in diesem Beispiel:

10  Function Global:Get-Wechselkurs {

11   Param(

12    [Parameter(Mandatory=$true,ValueFromPipeline=$true)]

13    [Double[]]

14    $Betrag,

15    [String]

16    $Waehrung="USD"

17   )

18  Begin {

19  # Einmalig zu Beginn aktuellen Wechselkurs bestimmen

20    $Xml=New-Object xml

21    $Xml.Load('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml')

22    $Wechselkurse=$Xml.Envelope.Cube.Cube.Cube

23    $Wert=$Wechselkurse | ? {$_.Currency -like $Waehrung} | select -expandproperty rate

24  }

25  Process {

26  # Betrag ausrechnen

27    $Betrag | Foreach-Object {

28     $_*$Wert

29    }

30  }

31  }

2.4.12.5.2            ForEach-Object

Ähnlich den Funktionen, kann man auch bei ForEach-Object mit Begin, Process und End arbeiten, die Syntax sieht aber etwas anders aus. Beispiel:

ls | foreach-object -Begin { "Nur am Anfang" } `
-Process
 { $_ | ? {$_.name -like "*.txt"} } `
-End
 { "Zum Schluß" }

Leider ist eine Kleinigkeit nicht offensichtlich. Eigentlich sollte im Process Bereich $_ natürlich das jeweilige Objekt enthalten. Das bedeutet, eigentlich sollte es auch ohne den roten Teil funktionieren. Sie können es ja gerne mal ohne das rote ausprobieren, es wird aber den Process Teil einfach komplett ignorieren. Schreiben Sie hingegen im Process Teil einfach nur $_.name (ohne das Where-Object = ?), klappt die Ausgabe des Dateinamens. Das gehört wohl in die Ecke „Mysterien der PowerShell“ ;-).

2.4.12.6                Speicher Probleme eindämmen

Je größer Ihre Skripte werden um so Speicherintensiver können diese werden. Insbesondere wenn viele Variablen deklariert werden.

Zunächst einmal sei hier noch einmal auf die Möglichkeit verwiesen, Variablen Speicher mittels Remove-Variable explizit freizugeben wie im Abschnitt Grundlagen der Variablenverwaltung beschrieben. Dies gibt den Speicher jedoch nicht sofort für andere Anwendungen frei. Eigentlich passsiert dabei am vom Prozess belegten Speicher erst einmal gar nichts. Der Speicher wird für zukünftige Speicheranfragen Ihres Skripts erst einmal vorgehalten um dann intern neu zugeordnet zu werden. Ein „Recycling“ Ihrer Variablen hat einen ähnlichen Effekt. Wenn Sie also zum zählen immer wieder das beliebte $i verwenden, wird auch der Speicher (den das vorangegangene $i verwendet hat) immer wieder automatisch freigegeben bzw. neu belegt. Wenn $i vorher von einem kompletten .NET-Objekt mit 100MB belegt wurde und Sie verwenden anschließend $i als Zähler, wird es automatisch zum 32-Bit Integer. Der restliche nicht mehr benötigte Speicher wird automatisch (für den Prozess – aber nicht das System!) freigegeben. Auch wenn Sie eine Funktion aufrufen, werden beim Beenden der Funktion der Speicher von allen innerhalb der Funktion genutzten Variablen (außer natürlich denen, die Sie durch einen Scope-Bezeichner sowieso außerhalb gesetzt haben) wieder dem Prozess zur Verfügung gestellt.

Sie sehen, dass sich PowerShell eigentlich schon ganz gut um die Speicherverwaltung kümmert. Trotzdem kann es sein, dass Sie Speicher von Ihrem Prozess ganz gerne wieder dem System zurückgeben möchten. Dafür hat .NET auch eine Müllabfuhr, den sogenannten Garbage-Collector (zu Deutsch: Müllsammler). Den können Sie so direkt aufrufen:

[System.GC]::Collect()

Danach werden alle durch Remove-Variable oder sonst freigegebenen Speicherbereiche von Ihrem Prozess an das System zurückgegeben. Das kostet natürlich auch ein paar CPU-Umdrehungen. Daher sollten Sie nicht wild nach jedem Remove-Variable die Müllabfuhr rufen, wenn Sie sowieso gleich wieder neue Variablen deklarieren. Abgesehen davon läuft der Garbage Collector sowieso in mehr oder weniger regelmäßigen Abständen im Hintergrund. Bei Ihnen daheim kommt sicherlich auch in regelmäßigen Abständen die Müllabfuhr um den Hausmüll entsprechend zu entsorgen. Der Sperrmüll wird wahrscheinlich auch 2 x im Jahr geholt. Manchmal benötigen Sie aber auch einmal zusätzlichen Termin, z.B. wenn Sie den Keller entrümpeln. Dann vereinbaren Sie bei der Stadt einen Termin zur Abholung. Genau so sollten Sie auch den manuellen Aufruf vom Garbage Collector einsetzen. Wenn Sie mehr zur Garbage Collection erfahren möchten:

https://msdn.microsoft.com/de-de/library/0xy59wtx(v=vs.110).aspx

2.4.13                     Kontrollfragen

Mit welchem Cmdlet können Sie die Verarbeitungsgeschwindigkeit überprüfen?

Was ist schneller Alias oder Cmdlet?

Wie würden Sie nach einem bestimmten Dateityp (z.B.: *.ps1 oder *.bat) suchen?

Wie können Sie die Verarbeitung von Funktionen beschleunigen?

Link zu den Lösungen für Skripting: Tuning

2.4.14                     Troubleshooting

Troubleshooting ist natürlich ein wichtiger Bestandteil einer Programmierumgebung. Hier werden Sie mit den Möglichkeiten der Fehlerbehandlung in Skripten der Version 1.0 vertraut gemacht. Erweiterte Methoden des Troubleshooting finden im nächsten Buchteil im Kapitel PowerShell 2.0.

Fehlermeldungen sind klasse! Denn so erfahren Sie, was schief gelaufen ist und können das Problem beheben. Gemein ist, wenn keine Fehlermeldungen auftauchen, das Skript aber nicht funktioniert wie gewünscht. Deshalb freuen Sie sich über jede Fehlermeldung!

2.4.14.1                Wie geht es weiter, wenn ein Fehler passiert?

In der automatischen Variablen $ErrorActionPreference ist festgelegt, wie standardmäßig mit Fehlern umgegangen wird. Der vorgegebene Wert Continue ist prima. Das bewirkt das Fehler angezeigt werden, aber wenn es irgendwie machbar ist, läuft ein Skript weiter. Alternativ gibt es noch die Möglichkeiten SilentlyContinue, Inquire oder Stop zuzuweisen. Bei SilentlyContinue wird die Fehlermeldung nicht angezeigt und das Skript versucht weiterzulaufen. Inquire zeigt den Fehler an und fragt den Benutzer ob er das Skript abbrechen oder weiterlaufen lassen möchte. Stop zeigt den Fehler an und bricht das Skript ab.

Ich schreibe hier die ganze Zeit, dass das Skript weiter ausgeführt wird, falls möglich. Da stellt sich natürlich die Frage, wann dies denn möglich ist. Das kommt auf die Schwere des Fehlers an. Daraus ergibt sich, ob es sich um einen Fehler mit oder ohne Abbruch handelt. In erster Linie wichtig ist an dieser Stelle zu erkennen, dass es zwei verschiedene Fehlertypen gibt.

2.4.14.2                Fehler ohne Abbruch

Die meisten Fehler sind Fehler ohne Abbruch des Skripts. Als Beispiel soll uns an dieser Stelle einmal das Auflisten eines nicht existenten Verzeichnisses dienen:

ls c:\Verzeichnisdasesnichtgibt

Über die automatische Variable $? können Sie abfragen, ob der vorangegangene Befehl erfolgreich war. Wenn Sie dies dann direkt nach dem fehlerhaften ls Befehl eintippen, sollte als Rückgabe False erscheinen. Wenn Sie es gleich noch einmal eintippen kommt True, weil die Abfrage, ob der letzte Befehl erfolgreich war funktioniert hat. Sie können also jederzeit mit $? ermitteln ob der vorangegangene Befehl erfolgreich war.

2.4.14.3                Fehler mit Abbruch

Fehler mit Abbruch sind selten. Sie kommen eigentlich nur dann vor, wenn die PowerShell den Faden verloren hat, also nicht einmal mehr ein Cmdlet, eine Variable, eine Funktion oder ein externes Programm finden kann. Also beispielsweise so etwas:

Keine Ahnung was der da wieder zusammen geschrieben hat ;-)

2.4.14.4                Fehler kontrolliert abfangen

Wie bereits erwähnt sind die meisten Fehler nicht kritisch, daher funktioniert auch eine Trap-Anweisung die eigentlich dazu gedacht ist Fehler kontrolliert zu bearbeiten nicht, da diese nur auf kritische Fehler anspringt. Ein kleines Beispiel soll Ihnen auch hier wieder verdeutlichen wo das Problem liegt:

"Beginn des Skripts!"

$Verzeichnis="Fröschegibtshiernicht"

ls $Verzeichnis 2> $null

if (!$?) {"Mist - Verzeichnis gibt's nicht!"}

"Ende des Skripts!"

So können Sie Fehler ohne Abbruch erfolgreich kontrollieren. Falls in Zeile 3 ein Fehler auftritt, wird die Standard Fehlermeldung unterdrückt. Dafür fragt die darauf folgende Zeile die Variable $? ab, ob der vorangegangene Befehl funktioniert hat. Sie sollten die selbst geschriebene Fehlermeldung sehen, sowie die Information, dass die erste und letzte Zeile des Skripts abgearbeitet wurde. Weisen Sie der Variablen $Verzeichnis ein Verzeichnis zu, das tatsächlich existiert (z.B. C:\), dann sehen Sie auch wieder die erste und die letzte Zeile. Dieses Mal aber, statt der selbst gestrickten Fehlermeldung, den Inhalt des Verzeichnisses.

Probieren wir einmal für Fehler eine Falle mit der Trap-Anweisung aufzustellen:

"Begin des Scripts!"

trap {"Achtung";exit}

$Verzeichnis="Fröschegibtshiernicht"

ls $Verzeichnis 2> $null

"Scriptende"

Wir sehen leider nur die erste und die letzte Zeile des Skripts, da der Fehler von Trap nicht erkannt wurde. Schreiben Sie doch einfach einmal vor den ls Alias die Worte: Keine Ahnung.

Keine Ahnung ls $Verzeichnis 2> $null

Wenn Sie nun das Skript erneut starten kommt die erste Zeile, die Falle schnappt zu, zeigt die Fehlermeldung und beendet wegen des 2. Befehls exit in der geschweiften Klammer das Skript. Daher wird die letzte Zeile des Skripts nicht mehr erreicht. Wenn Sie mögen, können Sie das exit auch gerne einal entfernen und dann werden Sie feststellen, dass auch die letzte Zeile noch angezeigt wird. Damit der Trap auch auf kleinere Fehler reagiert, müssen wir aus einem lapidaren Fehler, einen Fehler mit Abbruch machen. Dazu haben Sie zwei Möglichkeiten. Zum einen können Sie die automatische Variable $ErrorActionPreference auf Stop setzen:

$ErrorActionPreference=“Stop

Dadurch wird jeder Fehler zu einem Abbruchfehler. Die andere Möglichkeit besteht in einem Common Parameter namens –ErrorAction oder kurz -EA. Dann können Sie auf einem bestimmten Cmdlet den Schalter –EA Stop anwenden und dann werden Fehler an diesem Cmdlet als Abbruchfehler angesehen.

Auch hier können Sie im Beispiel Skript die Zeile mit ls wie folgt austauschen:

ls $Verzeichnis –ea stop

Haben Sie die Mäusefalle mit Trap erst einmal aufgestellt, gilt diese Anweisung von der Zeile an wo der Trap formuliert wurde bis zum Ende des Skripts. Haben Sie also einen Fehler beseitigt, aber die Trap Anweisung nicht entfernt, gibt es vielleicht später im Skript erneut einen Fehler und es wird wieder die Trap-Anweisung ausgeführt. Dann denken Sie u. U. wieder an das alte Problem und suchen den Fehler an der falschen Stelle. So etwas wie ein „Untrap“ gibt es leider nicht. So etwas in der Art erfüllt aber die Try-Catch-Finally-Anweisung, die es ab PowerShell 2.0 gibt (siehe nächster Buchteil, Kapitel PowerShell 2.0, TroubleShooting in Version 2.0).

2.4.14.5                Fehlerhistorie abfragen

Weiterhin von Interesse ist die Fehlerhistorie. Über aufgetretene Fehler wird in einer automatischen Variable eine Liste geführt. Die Variable ist ein Array und nennt sich $Error. Eine komplette Liste erhalten Sie indem Sie einfach $Error ausgeben. Den aktuellsten Fehler finden Sie in $error[0].

2.4.14.6                Fehler machen

Vielleicht möchten Sie ja selbst auch einmal eine Fehlermeldung generieren und diese an das Aufrufende Progrmm zurück liefern. Dazu bietet Ihnen die PowerShell 3 Möglichkeiten: Throw, Write-Error und Exit.

2.4.14.6.1            Exit

Exit ist nicht wirklich ein PowerShell Befehl, sondern ein Überbleibsel aus Urzeiten, dass in fast allen Progrmmiersprachen gleichermaßen eingesetzt werden kann. Ein einfacher Exit Befehl beendet einfach nur das aktuelle Programm. Allerdings kann man einen sogenannten Exit-Code in Form einer Zahl an das aufrufende Programm beim verlassen senden. Eine 0 bedeutet soviel wie: „Alles OK! Die Beendung des Programmes war so geplant.“. Alle anderen Zahlen lassen auf einen Fehler hindeuten. Welche Zahl, welchem Problem entspricht steht hoffentlich in der Dokumentation des Programmes das den Exit-Code ausgibt, denn dafür gibt es aufgrund der vielschichtigen Fehlermöglichkeiten keine Regeln. Eine 1 kann also in dem einen Programm bedeuten dass auf eine bestimmte Datei nicht schreibend zugegriffen werden kann, bei einem anderen bedeutet eine 1 vielleicht gar keinen Fehler, sondern einfach nur, dass eine Datei ordnungsgemäß vorhanden ist und der Exit-Code die einzige Möglichkeit zur Kommunikation mit der Außenwelt ist (DOS läßt grüßen). Wie man es einsetzt? Z.B.:

Exit 3

2.4.14.6.2            Write-Error

Mit Write-Error produzieren Sie einen Fehler ohne Abbruch. In der einfachsten Form geben Sie einfach nur einen Text an:

Write-Error “Shit happens!“

Damit wird ein Fehler Objekt basierend auf den Standard PowerShell Fehlermeldungen produziert und entsprechend Rot auf dem Bildschirm dargestellt. Falls Sie diese Anweisung in einem Skript einsetzen, wird die Ausführung des restlichen Skripts nach der Bildschirmausgabe fortgesetzt. Die Fehlermeldung wird dabei auch in die Array Variable $Error aufgenommen und kann entsprechend ausgewertet werden. Weitere Details erfahren Sie wie üblich mit Get-Help Write-Error.

2.4.14.6.3            Throw

Mit Throw produzieren Sie einen Fehler mit Abbruch. Im Gegensatz zu Write-Error wird das Skript nach der Anweisung nicht fortgesetzt. Throw stellt kein herkömmliches PowerShell Cmdlet mit etlichen Optionen dar. Auf Throw folgend kann nur noch festgelegt werden, was zurückgegeben werden soll. Beispiele:

Throw “Shit happens!“

oder

Throw (get-process powershell)

Das erste Beispiel liefert einfach nur den Text an das aufrufende Programm, während das 2. Beispiel Informationen zum PowerShell Prozess liefert.

Weitere Informationen liefert Get-Help about_Throw.

Wie bei Write-Error landet der produzierte Fehler in $Error. Gezielt nach dem Text, den Sie in der Fehlermeldung geschrieben haben, können Sie in der Eigenschaft Exception des Fehlerobjekts suchen, z.B. so:

$Error[0].Exception

$Error ist ja ein Array mit allen Fehlern die passiert sind. Den aktuellsten erhalten Sie durch die nachfolgende Angabe von [0] und wie gesagt steckt in der Eigenschaft Exception Ihr Text, falls der Fehler ausgelöst wurde. Eine entsprechende Abfrage könnte so aussehen:

If ($Error[0].Exception –like “Shit”) {“Dumm gelaufen”} else {“Alles gut”}

2.4.14.7                $Error zurücksetzen

Wenn Sie die Fehlerhistorie in der aktuellen Sitzung zurücksetzen (leeren/reseten) möchten, können Sie nicht Clear-Variable Error einsetzen, da dies zu einer weiteren Fehlermeldung führt und Ihre Historie stehen lässt. Das Zauberwort hier ist interessanter Weise: $Error.clear()

2.4.14.8                Debugging

Wie Sie bereits wissen sind Fehler bzw. Fehlermeldungen eine tolle Sache. Aber was, wenn der Fehler vor dem Bildschirm sitzt? Debugging wird genutzt um logische Fehler in Skripten zu entdecken. Die PowerShell führt also Ihre Skripte ohne Fehlermeldung aus, doch am Bildschirm erscheint nicht das gewünschte Resultat. Oft liegt dies an Variablen, die an der entsprechenden Stelle im Skript nicht den gewünschten bzw. erwarteten Inhalt haben. Ich persönlich bevorzuge mein Debugging selbst zu machen indem ich einfach an der problematischen Stelle im Skript den Inhalt von Variablen auf den Bildschirm schreiben lasse. Der Vollständig halber will ich aber die Debuggingmöglichkeiten der PowerShell hier kurz erwähnen.

2.4.14.8.1            Set-PSDebug -Strict

Vom Visual Basic her kennen einige unter meinen Lesern vielleicht die Anweisung Option Explicit. Fast genau dasselbe macht bei uns in der PowerShell die Anweisung: Set‑PSDebug –Strict. Dies hilft bei Tippfehlern in Variablennamen. Geben Sie für eine kleine Kostprobe folgendes ein:

$gibtsnicht

Set-PSDebug –Strict

$gibtsnicht

Das erste $gibtsnicht hat er einfach so geschluckt. Die Variable hat nie einen Wert zugewiesen bekommen. Die PowerShell hat die Variable einfach ignoriert. Nach der Anweisung Set‑PSDebug –Strict haben Sie der PowerShell aber erklärt, dass Sie auf die Verwendung von nicht definierten Variablen mit einer Fehlermeldung hingewiesen werden möchten. Daher hat die PowerShell beim 2. Versuch die Fehlermeldung präsentiert. Das hilft, wenn Sie sich gerne einmal vertippen. Also z.B. ein $j statt ein $i schreiben wie in diesem Beispiel:

For ($i=0;$i –lt 10;$i++) {$j}

Wenn Sie die Konsole seit der Set-PSDebug Anweisung noch nicht geschlossen haben, gilt das natürlich noch und die Schleife wird Ihnen 10 x wegen dem Lapsus $j statt $i in der geschweiften Klammer eine Fehlermeldung ausspucken. Sie haben im Schleifenkopf $i definiert und nicht $j, welches Sie versucht haben in der Schleife auszugeben. Wenn Sie zuvor ein $j angelegt hatten, klappt das natürlich so nicht, dann zeigt er Ihnen den Inhalt von $j 10 x hintereinander an. Sollte das der Fall sein, öffnen Sie einfach eine neue Konsole und probieren Sie es erneut. Das Set-PSDebug gilt wie üblich nur innerhalb des Bereiches in dem die Anweisung erfolgt. Wenn Sie es auf der Konsole eintippen gilt das nur in dieser Konsole und nicht in einer die Sie vielleicht parallel starten. Auch wenn Sie die Anweisung in einem Skript geben, gilt dies nur innerhalb des Skriptes. Ist das Skript beendet und Sie landen wieder an der Eingabeaufforderung, ist in der Eingabeaufforderung das Set-PSDebug –Strict nicht mehr aktiv.

2.4.14.8.2            Set-PSDebug –Trace

Mit dem Schalter –Trace können Sie den Debugging Level festlegen. D.h. wie detailliert die PowerShell den Ablauf Ihres Skriptes anzeigt bzw. verfolgt. Auf den Schalter -Trace folgen Werte zwischen 0 und 2. 0 schaltet die Ablaufverfolgung aus und dies ist die Standardeinstellung. Mit 1 wird Ihnen jede Skriptzeile die gerade durchlaufen wird am Bildschirm angezeit. Durch setzen einer 2 erhalten Sie zusätzliche Informationen, wie z.B. die aktuelle Belegung von Variablen.

2.4.14.8.3            Set-PSDebug –Step

Mit Set-PSDebug –Step gehen Sie schrittweise durch ein Skript. Das bedeutet, Sie müssen jede Zeile abnicken, dass es weiter gehen soll, oder ob Sie das Skript an dieser Stelle abbrechen möchten.

2.4.14.8.4            Set-PSDebug –Off

Set-PSDebug –Off schaltet den gesamten „Budenzauber“ wieder aus.

2.4.14.8.5            Debugger

Sie haben auch die Möglichkeit einen interaktiven Debugger zu aktivieren. Damit können Sie zur Laufzeit von Skripten deren Ablauf beeinflussen. Dazu müssen Sie Breakpoints oder zu Deutsch Haltepunkte setzen. Dazu dient das Cmdlet Set-PSBreakpoint. Ein Haltepunkt kann vor dem Aufruf eines Skripts gesetzt werden, beim Ausführen eines bestimmten Cmdlets oder aber auch bei Verwendung einer Variablen. Um einen Haltepunkt in einem Skript zu setzen geben Sie bevor Sie das Skript starten

Set-PSBreakpoint –Script NamedesSkripts.ps1 –Line 10

ein. Wenn Sie nun das Skript starten, würde ab Zeile 10 der interaktive Debugger aktiviert. Bei

Set-PSBreakpoint –Command Get-Help

Würde jedes Mal, wenn Sie Get-Help aufrufen der interaktive Debugger gestartet und bei

Set-Breakpoint –Variable Variablenbezeichner

wird jedes Mal bei Verwendung der entsprechenden Variable (ohne $ Zeichen angeben) der Debugger gestartet.

Mit dem Schalter –Action gefolgt von Anweisungen in runden Klammern können Sie noch zusätzliche Aktionen erfolgen lassen.

Ist der Debugger gestartet können Sie sich mit eintippen des Buchstaben h eine Hilfe anzeigen lassen, die Ihnen erklärt, was Sie hier tun können. Es stehen Aktionen wie

s – führe den Schritt aus

v – überspringe den nächsten Schritt

o – verlasse die aktuelle Funktion, oder das Konstrukt (z.B. eine Schleife)

c – mache im Skript weiter, ohne Debugger, bis zum nächsten Haltepunkt

q – beende das Skript und den Debugger

zur Verfügung. Wenn Sie einem Skript gleich in mehreren Zeilen Zwischenstopps einlegen möchten, geben Sie einfach hinter –Line mehrere Zeilennummern durch Komma getrennt an.

Mit Get-PSBreakpoint können Sie sich die von Ihnen gesetzten Haltepunkte auflisten lassen.

Durch Disable-PSBreakpoint können Sie einen Haltepunkt vorübergehend deaktivieren, um ihn später mit Enable-PSBreakpoint wieder zu aktivieren. Möchten Sie einen Haltepunkt komplett löschen, verwenden Sie Remove-PSBreakpoint.

Noch mehr Informationen erhalten Sie mit Help about_Debuggers.

2.4.15                     Kontrollfragen

In welcher automatischen Variablen ist festgelegt, was bei einem Fehler geschehen soll?

In welcher automatischen Variablen ist festgelegt, ob das vorangegangene Kommando erfolgreich war?

In welcher automatischen Variablen sind die bisher passierten Fehler notiert und von welchem Typ ist diese Variable?

Sie haben eine Trap-Anweisung in Ihr Skript eingebaut, weil Sie einen Fehler erwarten. Statt Ihren Trap-Code auszuführen, wird aber nur die standard PowerShell Fehlermeldung ausgespuckt. Was ist die wahrscheinlichste Ursache und wie können Sie dafür Sorge tragen, dass Ihr Trap-Code ausgeführt wird?

Was tun Sie damit die PowerShell Sie auf Tippfehler in Variablen aufmerksam macht?

Wie können Sie in einem Skript in der Zeile 5 und 10 den interaktiven Skriptdebugger aktivieren?

Wie verlassen Sie den interaktiven Debugger?

Wie können Sie im interaktiven Debugger die nächste Code-Zeile ausführen lassen?

Wie können Sie eine Code-Zeile im interaktiven Debugger überspringen?

Wie rufen Sie im interaktiven Debugger die Hilfe auf?

Link zu den Lösungen für Skripting: Troubleshooting

3        Unterschiede zwischen PowerShell Version 1.0, 2.0, 3.0, 4.0 und 5.0

3.1     PowerShell 1.0

Dieses Kapitel ist recht kurz und beschreibt einige Methoden die zwar unter neueren Versionen der PowerShell auch eingesetzt werden können, dort aber etwas anders gehandhabt oder erweitert werden.

3.1.1   Module in Version 1.0 (Snap-ins)

Hier werden die sogenannten PSSnapins erklärt. Unter PowerShell 1.0 war dies die einzige Möglichkeit den Befehlsumfang der PowerShell durch Dritthersteller zu erweitern. Ein PSSnapin kann z.B. einen Befehlssatz für den Active-Directory-Zugriff enthalten. Dies muss zunächst installiert und dann in die PowerShellumgebung integriert werden. Wie dies geschieht lesen Sie hier am Beispiel der Active-Directory Verwaltungstools von Quest.

Zunächst einmal müssen Sie sich die Erweiterungen von http://www.quest.com/powershell/activeroles-server.aspx herunterladen und installieren. Nach der Installation können Sie mittels Get-PSSnapin –registered herausfinden, welche Erweiterungen auf Ihrem System installiert sind und unter welchem Namen Sie die Erweiterung hinzufügen können. Wenn Sie nun nur Get-PSSnapin ohne Schalter ausführen, stellen Sie fest, dass nur die Erweiterungen gelistet sind, die zu PowerShell standardmäßig dazu gehören. Mittels Add-PSSnapin Quest.ActiveRoles.ADManagement können Sie die neuen Befehle für die Active Directory Verwaltung hinzufügen. Ein erneutes Get-PSSnapin listet nun zusätzlich die Quest Erweiterung als geladen auf. Aber welche neuen Cmdlets stehen Ihnen denn nun zur Verfügung? Um das herauszufinden geben Sie Get‑Command ‑PSSnapin Quest.ActiveRoles.ADManagement ein. Diese Befehle funktionieren, wie die restlichen Cmdlets auch. Sie können diese mit Get-Help entsprechend erforschen. Wenn Sie die Konsole schließen sind die Erweiterungen wieder weg und müssen bei einer neuen Konsole wieder geladen werden. Alternativ können Sie sich die Anweisung auch in Ihr Profil Skript schreiben. Die Quest-Tools haben gegenüber dem in PowerShell Version 2.0 von Microsoft bereits mitgebrachten Modul zur AD-Verwaltung den Vorteil, dass Sie ohne einen Windows Server 2008 R2 Domänencontroller auskommen und somit auch mit einem „alten“ 2003er oder 2008er Domänencontroller ohne Active Directory Management Gateway Service zusammen arbeiten.

3.2     PowerShell 2.0

Hier wird werden die Unterschiede und Vorteile bei der Befehlserweiterung und Troubleshooting im Gegensatz zur Version 1.0 dargestellt, ebenso wie die wichtigsten neu hinzugekommenen Cmdlets.

3.2.1   ISE

Ab PowerShell Version 2.0 wird auch das ISE (Integrated Scripting Environment) mitgeliefert. Dies ist ein Editor mit Debugger speziell für PowerShell Skripte. Anstatt die Debugging-Cmdlet im Skript zu verwenden, können Sie hier z. B. grafisch mit der Maus Ihre Breakpoints setzen. Bei der Arbeit mit Editoren (nicht nur ISE, auch z.B. PowerGUI) ist mir aufgefallen, dass irgendeine Art Zwischenspeicher verwendet wird, der zwischen den einzelnen Aufrufen nicht immer sauber gelehrt wird. So hatte ich schon einige Male das Phänomen, dass Fehler und deren Behebung durch alte Versionen in dem Zwischenspeicher verschleiert werden. Ich empfehle daher die Skripte nicht in Editoren zu testen, sondern zusätzlich zum Editor eine normale PowerShell-Konsole zu starten und von dort aus, die Skripte zu starten. Zum Bearbeiten der Skripte sind die Editoren, dank Syntax Highlighting und anderer netter Spielereien aber sehr gut zu gebrauchen. Anstatt viele Seiten mit Screenshots und seichtem Gewäsch zuzupflastern schauen Sie sich die ISE, doch einfach an. Wer Notepad bedienen kann, kommt auch schnell mit ISE klar. Unter Windows 7 und Server 2008 R2 muss ISE als Funktion (Systemsteuerung – Programme und Funktionen) /Feature (im Servermanager)nachinstalliert werden. Ansonsten können Sie während der Installation angeben, ob Sie ISE mitinstallieren möchten.

3.2.2   Troubleshooting in Version 2.0

Im Gegensatz zur relativ „dummen“ Trap-Anweisung kann Try-Catch-Finally eine ganze Menge mehr. Hauptvorteil ist, dass hier eingegrenzt werden kann von wo bis wo im Skript man mit einem Fehler rechnet. Die Grundlegende Syntax sieht so aus:

Try {

 Probiere den Code bis zur geschweiften Klammer zu

}

Catch {

 Wenn im Try-Abschnitt ein Fehler aufgetaucht ist, mache was hier steht.

}

Finally {

 Dieser Abschnitt ist optional. Wenn er vorhanden ist wird dieser Code auf jeden Fall ausgesführt, egal ob ein Fehler aufgetreten ist oder nicht.

}

Ein einfaches praktisches Beispiel könnte so aussehen:

"Begin des Skripts!"

try {

 von nix eine Ahnung, aber davon jede Menge

}

catch {

 "Achtung"

 $Error[0]

 exit

}

finally {

 "Ganz bestimmt!"

}

"Skriptende"

Die Zeile im Abschnitt Try konnte seltsamer Weise nicht ausgeführt werden ;-). Daher wurde der Code im Abschnitt Catch ausgeführt. Der zeigte den Text Achtung und die eigentliche Fehlermeldung an. Da die Fehlermeldung in weiß und nicht in Rot dargestellt wurde, kam die Fehlermeldung nicht direkt von der PowerShell, sondern wurde aus der Variablen $error gelesen. Interessant ist dabei, dass im Catch Abschnitt auch noch das Kommando exit steht, wodurch das Skript eigentlich beendet wird. Skriptende wird auch nicht mehr angezeigt, aber das was im Finally Abschnitt steht!

Schreiben Sie in der Code-Zeile des Try Abschnitts doch noch einmal den Alias ls vorne dran und setzen Sie den nachfolgenden Text in Gänsefüßchen:

 ls „von nix eine Ahnung, aber davon jede Menge

Dieses Mal ist die Fehlermeldung in Rot, weil Sie direkt von der PowerShell kommt. Allerdings hat der Catch-Block nicht statt gefunden und das Skriptende wurde erreicht. Auch das Finally wurde natürlich ausgeführt. Wir haben hier einen Fehler der nicht zum Abbruch führt und damit hat Try-Catch-Finally dieselben Probleme wie Trap. Um nun einen Fehler mit Abbruch daraus zu machen schreiben Sie im Try Abschnitt am Ende doch noch –ea stop dahinter:

 ls „von nix eine Ahnung, aber davon jede Menge“ -ea stop

Jetzt hat es wieder funktioniert, da Sie extra angegeben haben, dass Try in dieser Zeile auf den Fehler reagieren soll. Sollen die Try-Anweisungen auf jeden Fehler anspringen, können Sie das genau wie bei Trap, allgemein über die automatische Variable $ErrorActionPreference=“Stop festlegen.

3.2.2.1   Fehlermeldungen spezifizieren

Auch Fehler sind .NET-Objekte und zwar von der Klasse System.Management.Automation.ErrorRecord. Das bedeutet man kann diese mit Get-Member und Select auch wieder etwas näher untersuchen. Leider ist die Fehlerklasse des jeweiligen Fehlers nicht sofort aus der normalen Fehlermeldung ersichtlich. Sie erfahren die Fehlerklasse des letzten aufgetretenen Fehlers mithilfe der Fehlerhistorie:

$error[0].Exception.Gettype().Fullname

Wenn Sie nun einen Fehler in der Form:

ls –SchiessMichTot

provozieren sollten Sie nach der o. a. Abfrage auf $Error für den falschen Parameterbezeichner, die folgende Fehlerklasse erhalten:

[System.Management.Automation.ParameterBindingException]

Die Try-Catch-Finally Anweisung kann auch mehrere Catch Abschnitte enthalten, wobei Sie diese mit den Fehlerklassen näher spezifizieren können:

try {ls –SchiessMichTot}

catch [System.Management.Automation.ParameterBindingException] {"Parameter existiert nicht!"}

catch {"Ein unbekannter Fehler ist aufgetreten!"}

finally {$error[0].Exception.Gettype().Fullname}

Der Text aus der ersten Catch Anweisung wird angezeigt, weil dieser genau der vorangestellten Fehlerklasse entspricht. Aus dem Finally-Block erhalten Sie noch einmal die Ausgabe der Fehlerklasse zur Kontrolle.

Wenn Sie im Try Abschnitt das Minuszeichen entfernen versucht ls das Verzeichniss SchiessMichTot zu finden, was es wohl auf den wenigsten Rechnern geben dürfte und somit haben wir wieder eine ItemNotFoundException, die allerdings nicht zu einem Abbruch führen würde und daher springt keiner der Catch-Blöcke an.

Wenn Sie möchten, dass einer der Catch-Blöcke reagiert, müssen Sie wieder die Variable $ErrorActionPreference oder ein –ea stop hinten anfügen:

try {ls SchiessMichTot –ea stop}

Siehe da, ein unbekannter Fehler ist aufgetreten. Im zweiten Catch-Block ist eine Fehlerklasse angegeben und somit verhält sich dieses Catch, wie ein „Catch-All“. Er behandelt also alle Fehler, ausser denen die explizit spezifiziert wurden.

Wenn Sie mögen können Sie auch noch einmal alles aus dem Try-Block entfernen, ausser dem Wörtchen SchiessmichTot. Schauen Sie doch einmal wie die Fehlerbehandlung dann abläuft.

Wenn Sie einen größeren Try -Block angelegt haben und sich nicht sicher sind, woher der Fehler denn nun genau kommt auf den ein Catch anspringt, dann finden Sie weiterführende Informationen in $error[0].InvocationInfo (u.a. die Zeilennummer und den Name des Übeltäters -> Cmdlet).

3.2.3   Kontrollfragen

Wie sieht das grundlegende Konstrukt aus mit dem ab Version 2.0 Fehler behandelt werden können?

Muss jede Feherbehandlung einen Finally-Block haben?

Wie erfahren Sie den .NET-Fehlerklasse, wenn ein Fehler aufgetreten ist?

PowerShell 2.0: Troubleshooting

3.2.4   Allgemeine zusätzliche Befehle der Version 2.0

Hier finden Sie einige Informationen zu grundlegenden Erweiterungen in der Version 2.0.

3.2.4.1   Kommentarzeilen im Skript

Wie Sie bereits aus dem allgemeinen Teil des Buches wissen, können Sie Kommentare durch ein vorangestelltes #-Zeichen einleiten. Ab Version 2.0 haben Sie auch die Möglichkeit mehrere Zeilen in einem Skript als Kommentar-Bereich kenntlich zu machen:

<#

Viele bunte Zeilen

mit Kommentaren,

die nicht extra ein Kommentarzeichen

am Zeilenanfang benötigen

#>

Zwischen <# und #> können mehrere Zeilen als Kommentar direkt hintereinader weg geschrieben werden.

3.2.4.2   Die Ausgabe in einem Fenster auf der grafischen Oberfläche präsentieren

Ab der Version 2.0 steht Ihnen das Cmdlet Out-GridView zur tablearischen Ausgabe in einem grafischen Fenster zur Verfügung mit dem Sie obendrein noch Filterfunktionen anwenden können. Ein einfaches Beispiel dazu sieht so aus:

ls c:\windows | out-gridview

Voraussetzung, dass der Befehl klappt ist allerdings die Installation des .NET-Framework 3.5 mit SP 1 oder auf einem Server 2008 R2 das hinzufügen des Features .NET-Framework 3.51.

3.2.4.3   WMI Eigenschaften einstellen

Um WMI Eigenschaften einzustellen bringt PowerShell 2.0 das Cmdlet Set-WmiInstance oder kurz swmi mit. Damit lässt sich z.B. die Bezeichnung von Laufwerk C: ändern:

swmi -path "\\.\root\CimV2:win32_logicaldisk.DeviceID='C:'" `
-arguments @{Volumename="System
"}

Details über den Aufbau von WMI finden Sie im Buchteil Versionsunabhängig, im Kapitel über Objektorientierung und dort im Abschnitt WMI Objekte. Dem Parameter –Path folgt zunächst der Rechnername, der in diesem Fall einen Punkt darstellt für den lokalen Rechner. Weiterhin der Namespace in Form von root\CimV2 und die Objektklasse Win32_LogicalDisk. Um nur die Objektinstanz von Laufwerk C: zu bekommen, wird nach der Eigenschaft DeviceID auf Laufwerk C: hin gefiltert. Mit dem Schalter –Arguments können Sie dann der Eigenschaft VolumeName den neuen Inhalt System zuweisen.

3.2.4.4   Add-Type

Mit dem Add-Type Cmdlet, lässt sich das Laden von Assemblies vereinfachen. Statt:

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

können Sie einfach:

Add-Type -AssemblyName System.Drawing

schreiben.

3.2.5   Module in Version 2.0

Module sind die Nachfolger der PSSnapins. Sie lassen sich nicht nur einfacher anwenden, sondern auch ganz leicht selbst erstellen. Angefangen von den bei Windows mitgelieferten Modulen bis hin zur Erstellung eigener Module werden Sie alles in diesem Kapitel finden.

3.2.5.1   Standard Module

Welche Befehlserweiterungen auf Ihrem System zur Verfügung stehen ist abhängig davon, welches Betriebssystem Sie einsetzen und bei einem Server, welche Rollen installiert sind. So werden Sie auf einem Vista Client Applocker vergeblich suchen, weil dies ein neues Feature von Windows 7 und Server 2008 R2 ist. Auch in der Home Versionen von Windows 7 gibt es Applocker nicht. Welche Befehlserweiterungen Ihnen auf Ihrem System aktuell zur Verfügung stehen erfahren Sie durch Eingabe von:

Get-Module –list

Nur das Cmdlet zeigt Ihnen an welche Module bereits geladen sind. Da sollte im Normalfall keine Ausgabe erfolgen, denn noch haben Sie kein Modul geladen. Einige Module lassen sich nur korrekt importieren, wenn Sie die Skriptausführung z.B. mittels Set-ExecutionPolicy Cmdlet aktivieren.

Das Modul BitsTransfer sollte eigentlich überall vorhanden sein. Der Name des Moduls hört sich langweilig und kompliziert an. Ich glaube aber Sie werden sich sehr bald fragen, was Sie früher ohne dieses Ding gemacht haben. Bits steht für Background Intelligent Transfer Service. Bei der Installation des Acrobat-Reader taucht ein Häkchen auf mit dem Sie angeben können, das der Download nur stattfindet, wenn die Leitung frei ist, sprich nicht für irgendetwas anderes benötigt wird. Auch die Windows-Updates werden mithilfe von Bits eingespielt. Der Server muss diese Technik natürlich auch unterstützen, aber das ist bei den meisten professionellen Webservern der Fall. Greifen Sie sich zunächst einmal die zusätzlichen Cmdlets mit:

Import-Module Bits*

Sie dürfen die Namen der Module gerne mit *-Platzhalterzeichen abkürzen. Wenn Sie nun noch einmal nur Get-Module tippen, stellen Sie fest, dass BitsTransfer nun als Modul geladen ist. Welche Cmdlets Sie dazu bekommen haben verrät:

Get-Command –Module Bits*

Auch hier können Sie sich mehr Informationen zu den einzelnen Cmdlets mit Get-Help verschaffen. Im Kapitel mit Skripten arbeiten im Abschnitt über Funktionen, haben Sie ein eigenes Cmdlet zur Abholung von Websites geschrieben.

Um eine Internetseite herunterzuladen können Sie das Cmdlet Start-BitsTransfer verwenden:

Start-BitsTransfer "http://www.martinlehmann.de/wp” c:\test\website.htm

Damit wird meine Website bei Ihnen im Verzeichnis test in der Datei website.htm abgelegt. Das ist aber wieder einmal mehr mit Kanonen auf Spatzen geschossen. Wie wäre es einmal mit einem ordentlichen Download? Z.B. einem OpenSuSE–Linux DVD-Image? Das würde in der eben angegebenen Form nicht viel Spaß machen. Bislang haben Sie immer die Erfahrung gemacht, wenn die PowerShell-Konsole geschlossen wird, ist alles vorbei. Start-BitsTransfer verfügt über den Schalter –Asynchronous. Wenn Sie den noch hinten dran setzen, dürfen Sie Ihren PC sogar herunterfahren! Sobald der PC wieder eingeschaltet ist, wird der Download fortgesetzt, auch ohne dass Sie die PowerShell starten. Mit folgendem Befehl starten Sie den Download:

Start-BitsTransfer "http://ftp.uni-kl.de/pub/linux/opensuse/distribution/12.1/iso/openSUSE-12.1-DVD-i586.iso" c:\test\opensuse.iso –asynchronous

Jetzt wird immer, wenn die Leitung zum Internet frei ist ein Stück mehr der Datei heruntergeladen. Das geht natürlich auch in die andere Richtung. Des Weiteren funktioniert das auch mit ganz normalen Dateifreigaben. Mit etwas Nachdenken können Sie sich einen echt klasse Download-/Filemanager bauen. Starten Sie Ihren PC doch einmal neu und öffnen Sie anschliessend wieder die PowerShell Konsole.

Wenn Sie die Konsole schliessen ist das Modul bei einem erneuten Start nicht mehr geladen und muss erst erneut hinzugefügt werden. Es sei denn Sie packen das Laden des Moduls in eine Anweisung im Profil Skript.

Mit Get-BitsTransfer können Sie dann nachschauen ob der Download schon fertig ist. Wenn in der Spalte JobState Transferring steht dauert es noch. Wird aus dem Transferring ein Transfered, ist der Download abgeschlossen. Damit die Datei dann auch benutzbar ist, müssen Sie noch dieses Cmdlet absetzen:

Complete-BitsTransfer (Get-BitsTransfer)

Alle abgeschlossenen Downloads stehen damit als normale Datei am angegebenen Speicherort zu Ihrer Verfügung.

Möchten Sie einen Download abbrechen, können Sie das mit dem Cmdlet Remove‑BitsTransfer. Bei Get-Bitstransfer sehen Sie in der ersten Spalte eine JobId. Wenn Sie die hinter Remove-BitsTransfer übergeben wird dieser Download beendet. Wenn Sie alle abbrechen möchten:

Remove-BitsTransfer (Get-BitsTransfer)

Wenn Sie ein Modul wieder entladen möchten geht das mit:

Remove-Module NamedesModuls

Alle Module einzeln und vollständig zu erklären würde den Rahmen des Buches sprengen. Abgesehen davon gibt es auch noch die hervorragende Hilfe der PowerShell. Zu einigen Modulen werden Sie noch im Praxisteil des Buches weitere Informationen finden.

3.2.5.2   Module selbst entwickeln

Selbst Module mit eigenen Cmdlets zu erstellen ist ein Kinderspiel, wenn Sie den Abschnitt über Skripting und Funktionen im vorangegangnene Teil des Buches gelesen haben.

Als knappes Beispiel soll uns wieder der Befehl ll dienen:

<#

.SYNOPSIS

 Zeigt alle Eigenschaften von Dateien und Verzeichnissen an.

.DESCRIPTION

 Der Befehl soll den Alias aus SuSE-Linux ll implementieren. Auf einem SuSE-Linux System ist ll ein Alias auf ls –l. ls –l wiederum macht ein sogenanntes List Long, sprich es werden alle möglichen Informationen zu Verzeichnissen und Dateien dargestellt. Hier in der PowerShell wird ein Get-Childitem auf das entsprechende Verzeichnis durchgeführt und anschlissend an ein Select-Object * übergeben, was alle Informationen zu den von Get-Childitem gelieferten Datei- und Verzeichnisobjekten darstellt.

.EXAMPLE

 ll c:\

 Listet das Wurzelverzeichnis von Laufwerk C: mit allen Detailangaben.

.EXAMPLE

 „c:\“ | ll

 Übergibt das Verzeichnis über eine Pipe an den Befehl ll.

.LINK

 www.martinlehmann.de

#>

Function ll {

 Param(

  [Parameter(Mandatory=$true,ValuefromPipeline=$true)]

  [String[]]

  $Verzeichnis

 )

 Get-Childitem $Verzeichnis | Select *

}

Das Skript beginnt mit <# und leitet somit ein Kommentar bis zum schliessenden Element #> ein. Dieser Kommentar hat es aber im wahrsten Sinne des Wortes in sich. In diesem Kommentar-Bereich sind verschiedene Schlüsselbegriffe eingebaut, wie .Synopsis oder .Example. Auf diese Weise können die Hilfetexte für das Get-Help Cmdlet geschrieben werden. Welche Schlüsselbegriffe was genau machen erklärt Get-Help about_Comment_Based_Help. Ich muss mir ja hier nicht die Finger wund tippen um als Held da zu stehen, wenn es schon einen anderen Helden gab ;-).

Weiterhin interesant in diesem Zusammenhang ist auch die Kommentar-Anweisung #Requires. Damit können Sie ganz einfach die Fehlerbehandlung für Ihr Script durchführen, dass Befehle der Version 4 beinhaltet (z.B. DSC), jemand aber versucht das mit PowerShell Version 2.0 auszuführen. Ganz einfach am Anfang Ihres Scriptes diese Zeile rein:

#Requires –Version 4.0

Fertig! Das war’s! Versuch man nun das Script mit einer älteren Version auszuführen, gibt es automatisch eine durch PowerShell generierte Fehlermeldung, bevor es versucht dieser Version unbekannte Befehle zu interpretieren. Mit –Modules können Sie auch festlegen, ob bestimmte Module zur Scriptausführung benötigt werden. Noch mehr verrät: help about_requires.

Auf die Kommentarzeilen folgt eine ganz normale Funktionsdefinition, wie Sie dies bereits im vorangegangenen Buchteil kennengelernt haben. Die Besonderheit beim Modul liegt daran, dass Sie nicht wie in einem normalen Skript den Scope-Bezeichner Global: vor den Namen der Funktion schreiben müssen, damit die Funktion nach Ausführung des Skriptes auf der Konsole zur Verfügung steht. Bei Funktionen in einem Modul geht die PowerShell schon davon aus, dass Sie die Funktion als Cmdlet zur Verfügung stellen möchten und deklariert sie automatisch als global. Wenn Sie das nicht möchten, z.B. weil die Funktion nur Bestandteil einer anderen Funktion ist, können Sie durch einen Scope-Bezeichner wie Privat: festlegen, sodass diese Funktion eben nicht automatisch auf der Konsole zur Verfügung steht. Soweit die Theorie. In der Praxis klappt das mit dem Privat: in Modulen leider nicht. Aber ex gibt eine andere Möglichkeit gezielt Functionen zu veröffentlichen, bzw. zu verstecken mithilfe des Export-ModuleMember Cmdlet.

Export-ModuleMember geben Sie am Schluß Ihre Modules an und sagen damit aus, was Sie exportieren (sprich Global an die PowerShell Konsole übergeben) wollen. Dazu stehen Ihnen verschiedene Parameter zur Verfügung.

Export-ModuleMember –Function Funktion1,Funktion2 –Variable Var1,Var3

Angenommen Sie haben 3 Funktionen geschrieben mit den Namen Funktion1, Funktion2 und Funktion3, sowie 3 Variablen verwendet mit den Bezeichnern Var1, Var2 und Var3. Dann sind alle Funktionen und Variablen die Sie in Export-ModuleMember angeben öffentlich und alle die Sie nicht aufgeführt haben automatisch privat (also von der PowerShell Konsole aus nach laden des Moduls nicht sichtbar). In dem eben gelisteten Beispiel würden Sie nach einem Import‑Module die Funktionen: Funktion1 und Funktion2 sehen und benutzen können, aber nicht Funktion3. Bei den Variablen würden Sie Var1 und Var3 sehen und benutzen können, aber nicht Var2, da Sie die nicht im Export-ModuleMember gelistet haben.  Ähnliches gilt z.B. auch für Aliase. Mehr verrät wie üblich ein Get-Help Export-ModuleMember.

Sie können Ihr Skript zunächst ganz normal als .ps1-Datei abspeichern und testen. Wenn Sie zum Modul werden soll, brauchen Sie lediglich die Dateinamenerweiterung in .psm1 ändern und fertig. Ab sofort ist das kein PowerShell Skript mehr, sondern ein PowerShell Modul. Damit es als Modul vom PC erkannt wird muss die Datei nun nur noch im richtigen Verzeichnis liegen. Vielleicht ist Ihnen im Installationsverzeichnis der PowerShell (C:\Windows\System32\WindowsPowerShell\v1.0) schon einmal aufgefallen, dass es dort ein Unterverzeichnis Modules gibt. Hier müssen Sie nur noch ein Unterverzeichnis anlegen, dass genauso heißt wie Ihre psm1-Datei. Aber den Verzeichnisnamen ohne die Dateinamenerweiterung! Dann legen Sie Ihre Modul-Datei in das gleichnamige Verzeichnis und schon können Sie mit dem Cmdlet Get-Modules –list auch Ihr eigenes Modul sehen und natürlich auch mit Import-Module laden. Das ist das ganze Hexenwerk.

Damit würde Ihr Modul jedem Benutzer an diesem PC zur Verfügung stehen. Wenn Sie es nur für sich möchten, oder keine Berechtigungen auf das Systemverzeichnis haben um dort Ihr Modul abzulegen, können Sie die Modul-Dateien auch in Ihr Benutzerprofilverzeichnis legen.

Bei XP und Windows Server 2003 müssen Sie einen Ordner namens Modules unter:

C:\Dokumente und Einstellungen\Name des Benutzers\Eigene Dateien\WindowsPowerShell

anlegen. Bei Vista, Server 2008 und neueren Betriebssystemen unter:

C:\Benutzer\Name des Benutzers\Dokumente\WindowsPowerShell

In diesem Ordner müssen wieder genauso wie im Modules-Verzeichnis des Systems vorgehen, sprich noch einen Unterordner mit dem Namen der psm1-Datei anlegen und da Ihre Modul-Datei hinein kopieren.

3.2.5.3   Bestehende Cmdlets erweitern (ProxyCmdlets)

Als ich vor kurzem eine OU im AD löschen wollte, bin ich über ein kleines Problem gestolpert. Es gibt das Cmdlet Remove-ADOrganizationalUnit im Modul für ActiveDirectory. Es hat zwar einen Schalter –Recurse (um UnterOUs zu löschen), doch leider keinen Schalter –Force. Unter –Force hätte ich mir gewünscht, dass OUs die mit einem Löschschutz versehen sind, ebenfalls gelöscht werden.

Natürlich können Sie sich nun eine Funktion schreiben, die dies für Sie situationsbedingt erledigt. Doch richtig cool wäre doch, wenn Sie das bestehende Cmdlet, mit all seinen Optionen, einfach um den zusätzlichen Parameter –Force erweitern.

Microsoft hat hierfür ab PowerShell Version 2.0 ein .NET-Objekt bereitgestellt, mit dem Sie sich einfach den Code für ein Proxy-Cmdlet generieren lassen können. Möchten Sie also Get-ChildItem erweitern, baut Ihnen dieser 2-Zeiler ein entsprechendes Skript, dass Sie nur noch erweitern müssen:

$MetaData = New-Object System.Management.Automation.CommandMetaData(Get-Command Get‑ChildItem)

[System.Management.Automation.ProxyCommand]::Create($MetaData) > GetChildItemProxy.ps1

Dazu gibt’s auch von den Scripting-Guys noch jede Menge Erklärungen.

3.2.6   Kontrollfragen

Wie stellen Sie fest, welche Module Ihnen zur Verfügung stehen?

Wie stellen Sie fest welche Module bereits geladen sind?

Wie laden Sie ein Modul?

Wie lautet die Dateinamenerweiterung für ein Modul?

Wo müssen Sie Ihre Modul-Datei ablegen, damit Sie allen Benutzern zur Verfügung steht?

Wo müssen Sie Ihre Modul-Datei ablegen, damit Sie einem bestimmten Benutzer zur Verfügung steht?

Wie könnnen Sie in einem selbst erstellten Modul die Hilfetexte für Ihre Cmdlets generieren?

PowerShell 2.0: Module

3.2.7   PowerShell Remote Zugriff

Im Kapitel über Remote Zugriff erfahren Sie alles rund um den Fernzugriff mittels WinRM. Remoting mittels WMI ist eine andere Geschichte, die bereits im Buchteil Versionsunabhängig, im Kapitel Objektorientierung und dort im Abschnitt WMI Objekte behandelt wurde. Mit WinRM können Sie direkt PowerShell Cmdlets auf anderen PCs ausführen. Wie Sie einzelne Befehle auf 500 Rechnern gleichzeitig ausführen, oder sich einfach „nur“ auf die Konsole eines anderen Rechners schalten erfahren Sie in diesem Kapitel.

3.2.7.1   Voraussetzungen schaffen und konfigurieren

Wenn Sie PowerShell 2.0 auf einem älteren Betriebssystem nachrüsten, wird Ihnen aufgefallen sein, dass Sie PowerShell 2.0 nicht einzeln herunterladen können, sondern immer nur in Verbindung mit WinRM 2.0 (Windows Remote Management). Dies stellt die Technologie für den Remote Zugriff zur Verfügung. Zur Übertragung wird das SOAP-Protokoll über HTTP verwendet. In der Version 1.0 von WinRM wurde sogar noch der Port 80 (Standard für HTTP-Übertragungen) verwendet bzw. 443 für verschlüsselte Übertragungen mittels HTTPS. In der Version 2.0 hat man zwar das HTTP Übertragungsprotokoll beibehalten, es wird aber nicht mehr über Port 80, sondern über 5985 abgewickelt. Als Ersatz für 443, die verschlüsselte Übertragung, dient 5986. Das bedeutet weiterhin, dass falls sich Firewalls (dazu zählt auch die eingebaute Desktopfirewall von Windows) zwischen den beiden PCs befinden die miteinander kommunizieren sollen, die Ports entsprechend freigeschaltet werden müssen.

Die Übertragung über HTTP ist bereits verschlüsselt. Die Befehle können daher nicht mitgeschnitten oder bei der Übertragung manipuliert werden. Die Übertragung per HTTPS fügt nur noch eine zusätzliche Sicherheitsstufe durch eine zweite Verschlüsselung hinzu. Bei der Übertragung per HTTPS müssen Sie für die Computer Zertifikate für die Autenthifizierung und Verschlüsselung ausgestellt werden.

3.2.7.1.1    Remoting mittels Cmdlet aktivieren

Aus Sicherheitsgründen ist die Remoteverwaltung deaktiviert. Im einfachsten Falle verwenden Sie das Cmdlet Enable-PSRemoting, um die Remoteverwaltung zu aktivieren. Das funktioniert aber nur auf einer PowerShell Konsole, die Sie als Administrator mit erhöhten Rechten gestartet (Rechtsklick auf das PowerShell Symbol und Als Administrator ausführen anklicken) haben. Disable-PSRemoting macht das Ganze natürlich wieder rückgängig. Auch bei aktiver Remoteverwaltung ist es nur Benutzern, die auf dem Zielsystem zur Gruppe der lokalen Administratoren gehören gestattet, remote Befehle per PowerShell zu senden.

Duch das Cmdlet Enable-PSRemoting wird der Dienst Windows-Remoteverwaltung auf automatisch gesetzt und gestartet. Die Einstellung automatisch am Dienst bewirkt, dass er auch nach einem Neustart des Betriebssystems wieder gestartet wird. Des Weiteren wird eine Ausnahme für die Window Firewall auf dem System eingerichtet, damit die Verbindung zustande kommen kann und letztendlich noch der Endpunkt registriert, sprich die Verbindung der PowerShell mit dem Port hergestellt. Wenn Sie nur einzelne Systeme dafür freischalten möchten, ist dies die komfortableste Möglichkeit. Wenn Sie auch nicht tiefer in die manuelle Konfiguration einsteigen möchten, können Sie auch gleich im Abschnitt Cmdlets oder Skripte auf mehreren Rechnern ausführen lassen weiterlesen.

3.2.7.1.2    Remoting von remote aktivieren

Also eigentlich geht das ja nicht, aber:

Invoke-WmiMethod Win32_Process Create -Args "powershell –noprofile`
-command Enable
-PsRemoting -Force" -Computer NameDesComputers

Durch diesen WMI-Methoden-Aufruf können Sie auf dem angegebenen Zielrechner das PowerShellRemoting aktivieren, wenn Sie dort zum „Club“ der lokalen Administratoren gehören.

3.2.7.1.3    Remoting über Active Directory Gruppenrichtlinien aktivieren

Starten Sie die Gruppenrichtlinienverwaltungskonsole und suchen Sie die Organisationseinheit aus welche die Computerkonten enthält, für die die Remoteverwaltung mittels PowerShell aktiviert werden soll und erstellen Sie dort ggf. eine neue Gruppenrichtlinie.

Die Ausnahme für die Windows Firewall Regel können Sie in der Gruppenrichtlinie unter diesem Punkt vornehmen:

Computer\Richtlinien\Windows-Einstellungen\Sicherheitseinstellungen\Windows-Firewall mit erweiterter Sicherheit\ Windows-Firewall mit erweiterter Sicherheit\Eingehende Regeln

Führen Sie an dieser Stelle einen Rechtsklick aus und klicken Sie Neue Regel an. Wählen Sie Vordefiniert und dort Windows-Remoteverwaltung aus und klicken Sie 2 Mal auf Weiter und dann auf Fertigstellen. Wenn Sie nicht die Standardports verwenden möchten, müssen Sie selbst eine entsprechend angepasste Regel erstellen.

Den Dienst für die Windows-Remoteverwaltung können Sie unter diesem Punkt finden:

Computer\Richtlinien\Windows-Einstellungen\Sicherheitseinstellungen\Systemdienste

Wählen Sie dort den Dienst Windows-Remoteverwaltung aus, wählen Sie Diese Richtlinieneinstellung definieren und den Startmodus Automatisch aus. Klicken Sie auf OK.

Um den Listener zu aktivieren müsen Sie noch in diesem Abschnitt angeben von welchen IP-Adressen aus der Remotezugriff möglich sein soll:

Computer\Richtlinien\Administrative Vorlagen\Windows-Komponenten\Windows-Remoteverwaltung (WinRM)\WinRM-Dienst\Automatische Konfiguration von Listenern zulassen

Klicken Sie auf Aktiviert und tragen Sie in dem darunter liegenden Feld den gewünschten Adressbereich der Hosts ein, auf die der Zugriff gestattet werden soll (z.B.: 192.168.0.1‑192.168.0.254, 192.168.3.20-192.168.3.20). Wie im Beispiel zu sehen, müssen auch einzelne IP‑Adressen (192.168.3.20) als Bereich angegeben werden. Weiterhin anzumerken ist, dass es die IP‑Adressen der Systeme sind auf die zugegriffen wird und nicht die von denen aus die Verbindung aufgebaut wird!

3.2.7.1.4    Remoting in einer Arbeitsgruppe

Wenn die PCs nicht zum selben Active Directory gehören scheidet die einfache Aktivierung mittels Enable-PSRemoting oder Gruppenrichtlinien im Active Directory aus. Dann bleibt nur manuell Hand an das Webservice Management zu legen. Wenn Sie das nicht vorhaben, tun Sie sich diesen Abschnitt nicht an und lesen Sie gleich bei Cmdlets oder Skripte auf mehreren Rechnern ausführen lassen weiter.

Zunächst starten Sie die PowerShell Konsole als Administrator, wechseln Sie in das PSLaufwerk WSMan: und dort in den entsprechenden Unterordner:

cd WSMan:\localhost\client

Ein Get-ChildItem an dieser Stelle sollte in der Zeile Trusted Hosts und in der Spalte Value einen leeren Bereich enthalten. Hier stehen IP-Adressen oder Rechnernamen mit denen sich Ihr PC verbinden darf. Sollten hier bereits Angaben drinstehen, wollen Sie diese wahrscheinlich nicht überschreiben. Wenn Sie hier Werte hinzufügen möchten, müssen Sie bei dem nachfolgenden Befehl den Schalter –Concatenate mit angeben, ansonsten werden die bestehenden Einträge ersetzt, anstatt die neuen angehängt. Um auf einem Rechner außerhalb Ihrer Domänenumgebung zugreifen zu können, geben Sie den Befehl in folgender Syntax ein:

Set-Item .\TrustedHosts Name oder IP-Adresse des Rechners mit dem Sie sich verbinden möchten.

Ein Beispiel um auf einen Webserver Namens IIS zugreifen zu können:

Set-Item .\TrustedHosts IIS

Wenn Sie zusätzlich auch auf das komplette Subnet 192.168.0.0/24 den Zugriff gestatten möchten:

Set-Item .\TrustedHosts 192.168.0.* -Concatenate

Ohne den Schalter –Concatenate wäre nur noch das Subnet zulässig. Der Zugriff auf IIS wäre entfernt worden. Wenn Sie den Zugriff wieder allen entziehen möchten, setzen Sie einfach einen Leerstring:

Set-Item .\TrustedHosts ““

Möchten Sie nur einen einzelnen Wert aus der TrustedHosts entfernen:

$Original=(Get-Item .\TrustedHosts).Value

Set-Item .\TrustedHosts (($Original.split(",") | ? {$_ -notcontains` "ZuEntfernendeIPoderName"}) -join ",")

Im Unterordner DefaultPorts könnten Sie auf die gleiche Weise die Ports für die Netzwerkkommunikation ändern. Dabei sollten Sie nicht vergessen auch die Firewall-Regeln anzupassen!

Wenn Sie lieber mit Zertifikaten über HTTPS statt der IP-Adresse arbeiten möchten, müssen Sie sich zunächst ein Zertifikat zum Zwecke der Computer-Authentifizierung beschaffen und im Zertifikatsspeicher des Computers (nicht des Benutzers!) ablegen. Dies können Sie dann mittels des folgenden Cmdlet auf dem fernzusteuernden Rechner registrieren:

New-WSManInstance winrm/config/Listener `
–SelectorSet @{Address=’*’;Transport=”HTTPS”} `
–ValueSet @{
Hostname
="Rechner1.Domäne.Land";
CertificateThumbprint
="DigitalerFingerabdruckDesZertifikats"
}

Das Sternchen * in der 2. Zeile legt fest, von wo aus Sie sich mit dem Rechner verbinden können. In diesem Falle sind keine Einschränkungen gemacht. Auch hier ließen sich statt des Sternchens IP-Adressen angeben.

Hinter Hostname müssen Sie den Computernamen angeben auf den das Zertifikat ausgestellt wurde.

Hinter CertificateThumbprint müssen Sie den „Fingerabdurck“ des Zertifikates angeben (finden Sie im Zertifikat).

Um sich dann verbinden zu können dürfen Sie den Parameter –UseSSL nicht vergessen. Beispiel:

Enter-PSSession –ComputerName Rechner1.Domäne.Land -UseSSL

Über Zertifikate selbst ließe sich noch einmal so ein Buch füllen, daher möchte ich an dieser Stelle nicht weiter darauf eingehen und lieber auf einschlägige Literatur zu diesem Thema verweisen.

3.2.7.2   Cmdlets oder Skripte auf mehreren Rechnern ausführen lassen

Nachdem die u.U. etwas komplizierte Konfiguration abgeschlossen ist, wieder zu den einfachen Dingen des Lebens. Wenn Sie ein Cmdlet gerne auf einem anderen Computer ausführen lassen möchten, aber das Cmdlet keinen Schalter –Computername mitbringt, dann können Sie einfach das Cmdlet Invoke-Command aussen herum bauen. Die Syntax ist denkbar einfach:

Invoke-Command {Einzelnes Cmdlet oder Befehlskette, die remote ausgeführt werden soll} ‑Computername Rechnername1,Rechnername2,usw…

So hat Beispielsweise das Get-ChildItem Cmdlet keinen Schalter –Computername. Um nun den Inhalt von Laufwerk C: anderern Rechner einzusehen könnte das so aussehen:

Invoke-Command {Get-Childitem C:\} –Computername Rechner1, Rechner2

Auf Ihrem Bildschirm tauchen dann die Wurzelverzeichnisse beider Rechner auf. Sollte einer der Rechner ein Authentifizierungs Problem melden, müssen Sie einen Benutzernamen und ein Kennwort mitgeben, von einem Benutzer der auf dem Zielsystem zur Gruppe der lokalen Administratoren gehört. Dazu können Sie z.B. folgenden Schalter verwenden ‑Credential (Get‑Credential). Selbstverständlich können Sie auch die Anmeldeinformationen vorher in einer Variablen ablegen und diese an den Schalter übergeben:

$Anmeldeinfo=Get-Credential

Invoke-Command {Get-Childitem C:\} –Computername `
Rechner1, Rechner2 –Credential
 $Anmeldeinfo

Wenn Sie ein ganzes Skript remote ausführen lassen möchten, geht das auch ganz einfach:

Invoke-Command –Filepath C:\IhrSkript.ps1 –Computername Rechner

Denken Sie dabei aber immer daran, dass ein anderer Rechner eben ein anderer Rechner ist. Wenn Sie vom Skript aus beispielsweise auf eine Datei zugreifen, die zwar auf Ihrem PC vorhanden ist, aber nicht auf den Remoterechnern wird Ihr Skript wahrscheinlich schief laufen. Auch Variablen werden nicht in die RemoteShell vererbt!

$a=“test“
Invoke-Command {“Nicht existente Variable
: $a“} –Computer Rechner

Diese Zeile wird nur den Text ausgeben, aber nicht den Inhalt von $a, weil $a in der RemoteShell nicht exisitiert, sondern nur auf dem aufrufenden Rechner. Mit Enter-PSSession haben Sie keine direkte Möglichkeit Daten zu übergeben, höchstens mit Tricks (z.B. Infos in einer Datei ablegen und dann aus der Remote-Session den Inhalt der Datei über’s Netzwerk laden). Mit Invoke‑Command können Sie aber direkt Infos mit Hilfe des Schalters –ArgumentList übergeben:

Invoke-Command {“Inhalt von `$a wird zu `$args: $args“} `
-Computer
 Rechner1 -ArgumentList $a

Des Weiteren sollten Sie beachten was Sie innerhalb der geschweiften Klammer angeben und was ausserhalb steht:

Invoke-Command {Get-Childitem C:\} –Computername Rechner1,`
Rechner2
| Out-File
 C:\test\Inhalt.txt

Holt den Verzeichnisinhalt der beiden PCs ab und schreibt es auf Ihrem Rechner in die Datei Inhalt.txt.

Invoke-Command {Get-Childitem C:\ | Out-File `
C:\
test\Inhalt.txt} ‑Computername Rechner1, Rechner2

Holt den Verzeichnisinhalt auf dem jeweiligen PC ab und schreibt es auf den RemotePCs in die entsprechende Datei. Wenn es auf diesen PCs gar kein Verzeichnis test auf Laufwerk C: gibt, haben Sie schon gleich einen Fehler.

In Sachen Tuning ist es natürlich auch sinnvoll möglichst viel remote erledigen zu lassen, statt auf dem FernsteuerungsPC. Zum einen können Sie die CPUs der Fremdsysteme nutzen, zum anderen verringert sich die Netzwerklast, wenn Sie nur die benötigten Daten zum FernsteuerungsPC zurück schicken.

Langsam:

Invoke-Command {Get-Process} -Computer name1,name2,name3 | `
Where-Object
 {$_.VM -gt 30MB} | sort PSComputerName | select Name,VM

Schnell:

Invoke-Command {Get-Process | select Name,VM | Where-Object `
{$_.VM -gt 30MB}} -Computer
 name1,name2,name3 | sort PSComputerName

Apropos PSComputerNameWenn die Objekte zwischen den PCs übertragen werden, bekommen diese ein paar Eigenschaften dazu, verlieren aber auch Informationen. Eine der zusätzlichen Eigenschaften ist PSComputerName. Anhand dieser Eigenschaft können Sie erkennen von welchem (bei mehreren) RemotePC das jeweilige Objekt ist. Verloren gehen alle Objektmethoden. Möchten Sie auf Objektmethoden zugreifen muss dies innerhalb der gescheiften Klammern (also noch auf den RemotePCs) erfolgen. Sind die Objekte erst einmal auf Ihrem Rechner (nach der geschweiften Klammer zu) sind die Methoden nicht mehr verfügbar.

Wenn Sie eine Textdatei mit Computernamen in jeder Zeile vorliegen haben, können Sie die auch gerne direkt in das Invoke-Command Cmdlet importieren:

Invoke-command {Remote-Befehle} –Computer (gc DateiMitComputernamen.txt)

Da können natürlich 500 PCs drin stehen. Was ist denn dann im Netzwerk los? Keine Bange! Generell werden max. 32 PCs gleichzeitig befragt. Erst wenn die Ihre Ergebnisse zurück geliefert haben, kommt die nächste „Rutsche“. Sie können das Ganze mit dem Schalter –ThrottleLimit feintunen. Wenn Sie nur 10 oder gar 100 PCs auf einmal befragen möchten, geben Sie einfach die entsprechende Zahl hinter dem Schalter –ThrottleLimit an.

3.2.7.3   Die Kommandozeile auf einen entfernten Rechner umschalten

Um sich auf einen anderen Rechner direkt drauf zu schalten können Sie das Cmdlet Enter‑PSSession verwenden. Geben Sie dazu einfach den Namen oder die IP-Adresse des Remoterechners hinter dem Cmdlet an:

Enter-PSSession Rechnername

oder

Enter-PSSession IP-Adresse

Auch hier können Sie den Schalter –Credential wie beim Invoke-Command verwenden. Sowohl Invoke-Command, als auch Enter-PSSession haben natürlich noch etliche weitere Schalter die im jeweiligen Fall weiterhelfen können. Wenn Sie da einmal mit Get-Help nachschauen werden Sie feststellen, dass das ganz schön viele sind. Da wünscht man sich eine Möglichkeit die Schalter schon irgendwie vorzubelegen. Oft wird hier zum Cmdlet New-PSSession geraten, mit dem die Verbindung aufgebaut und in einer Variablen abgelegt wird. Der Aufruf zum Verbindungsaufbau wäre dann statt mit Computername oder IP-Adresse einfach mit der Sitzungsvariable:

Enter-PSSession $Verbindungsvariable

Ich habe aber eine wesentlich bessere Idee. Im ersten Buchteil, im Kapitel Objektmethoden in der Praxis und dort im Abschnitt über Besondere Variablentypen: Hash, Splatting und Unterausdrücke haben Sie das Splatting kennen gelernt. Packen Sie doch alle Schalterinformationen in einen Hash und „splatten“ Sie ihn an Enter-PSSession:

Enter-PSSession @Splatvariable

Das hat den ganz gravierenden Vorteil, dass die Sitzungen nicht den ganzen Tag offen stehen und nur dann aufgebaut werden, wenn man sie wirklich braucht. Das hat Sicherheits- und Netzwerktechnisch nur Vorteile, aber auch einen kleinen Haken. Hierzu ein kleiner Vergleich.

Beispiel - Remoting 1:

Invoke-Command {$Test=1} –ComputerName Rechner1

Invoke-Command {$Test} –ComputerName Rechner1

Beispiel - Remoting 2:

$Verbindungsvariable=New-PSSession –Computername Rechner1

Invoke-Command –Session $Verbindungsvariable  {$Test=1}

Invoke-Command –Session $Verbindungsvariable {$Test}

Remove-PSSession $Verbindungsvariable

In beiden Beipielen werden hintereinander die Cmdlets Invoke-Command verwendet. Im ersten Cmdlet wird die Variable $Test gesetzt und im zweiten ausgelesen. Im Beispiel Remoting 1 ist beim 2. Invoke-Command der Inhalt von $Test  (bzw. die komplette Variable) unbekannt, da hier nach dem 1. Invoke-Command die „bestehende“ PowerShell Konsole geschlossen und beim 2. Invoke-Command eine neue geöffnet wird. Im Beispiel Remoting 2 wird die Konsole durch New‑PSSession auf dem entfernten Rechner erstellt und erst mit Remove-PSSession wieder beendet. Falls Sie tatsächlich auf die Verbindungsvariable zurückgreifen möchten, können Sie sich mittels Get-PSSession einen Überblick verschaffen, was noch so alles an Sitzungen offen steht und Ressourcen frisst. Spätestens wenn Sie zehn Sitzungen vergessen haben zu schließen, werden Sie wissen was Ressourcen sind, denn eine elfte Sitzung werden Sie nicht mehr aufbauen können. ;-)

Es gibt noch ein paar Einschränkungen für Remotesitzungen. Wenn Sie auf Ihrem Rechner beispielsweise das Modul für BitsTransfer geladen haben, stehen Ihnen diese Cmdlets dieses Moduls in der Remotesitzung nicht zur Verfügung. Genauso umgekehrt. Module die Sie in der Remotesitzung geladen haben, gehen nicht mit zurück auf Ihren Rechner, wenn Sie die Sitzung wieder verlassen. Des Weiteren werden auch Einstellungen aus einem Profil Skript nicht mitgenommen. Ebensowenig wird ein auf dem Remoterechner vorhandenes Profil Skript ausgeführt. Also gar keine Profil Skripte um es kurz zu formulieren. Oder noch einmal ganz einfach zusammengefasst: In der Remotesitzung haben Sie eine ganz normale Standard-PowerShell Umgebung vorliegen, ohne Schnick und ohne Schnack. Alles was Sie dort an Komfort haben möchten, müssen Sie sobald Sie auf dem Zielsystem sind, manuell (z.B. ein Profil Skript) ausführen.

Eine Remotesitzung wieder verlassen können Sie mithilfe des Cmdlet Exit-PSSession. Achtung! Nicht einfach nur Exit, denn damit schliessen Sie den PowerShell-Prozess auf dem Fernsteuerungsrechner, nicht dem Zielsystem. Rein theoretisch können Sie sich von einem Rechner auf den Sie sich remote aufgeschaltet haben wieder auf einen anderen Rechner weiterverbinden. Danach am besten noch weiter auf Ihr ursprüngliches System von dem Sie gestartet sind. Tun Sie es nicht! Egal ob Sie dabei ein Ringelpitz aufgebaut haben oder sich einfach über 2 Hops verbinden. Schliessen Sie immer erst eine Sitzung, bevor Sie sich auf einen anderen Rechner verbinden. Ihre Netzwerktruppe wird es Ihnen danken.

3.2.7.4   Durch Windows Remoting Cmdlets auf einem Rechner ausführen, der dazu eigentlich nicht in der Lage ist

Auf Windows XP können Sie die Active Directory Cmdlets normaler Weise nicht ausführen. Sie haben aber die Möglichkeit auf einem Domänencontroller die Active Directory Cmdlets als Modul zu importieren. Dazu müssten Sie aber an einem Domänencontroller sitzen oder sich per Enter‑PSSession drauf schalten. Alternativ können Sie aber auch eine Sitzung auf dem Domänencontroller in die lokale PowerShell Konsole importieren. Das geht folgendermaßen:

$Sitzung=New-PSSession –Computer $DC –Credential $AnmeldeInfo

Invoke-Command {Import-Module ActiveDirectory} –Session $Sitzung

Import-PSSession –Session $Sitzung –Module ActiveDirectory

In der ersten Zeile wird eine Sitzung mit dem Domänencontroller hergestellt und in der Variablen $Sitzung hinterlegt. Dem Schalter -Computer folgt die IP-Adresse oder der Name des Domänencontrollers. In diesem Beispiel habe ich die Variable $DC verwendet, die natürlich vorher definiert werden muss (z.B. $DC=192.168.0.5)! Mit dem Schalter –Credential können Sie Anmeldeinformationen (aus den vorangegangenen Beispielen) für einen Domänenadministrator übergeben, falls Sie an Ihrem Computer nicht bereits als Domänenadministrator angemeldet sind und die PowerShell Konsole über Als Administrator ausführen gestartet haben.

Das Invoke-Command in der zweiten Zeile lädt in der angegebenen Sitzung das ActiveDirectory Modul.

Zu Schluß importieren Sie sich aus der Sitzung die Cmdlets aus dem zuvor geladenen ActiveDirectory Modul in Ihre lokale Konsole.

3.2.7.5   Multi-Hop Remoting

Grundsätzlich ist es auch Sicherheitsgründen nur möglich von einem Rechner auf einen anderen zuzugreifen. In größeren Firmen ist es nicht unüblich, dass Server in einem geschützten Bereich stehen und von Clients aus nicht direkt erreichbar sind, sondern nur über einen Gateway oder Jumphost genannten Server.

Wenn Sie also nun versuchen einen Ihrer Server im geschützten Bereich per PowerShell direkt zu erreichen wird das durch die Firewall geblockt. Sie können sich von Ihrem Client aus nur mit dem Jumpserver verbinden. Das geht natürlich auch per PowerShell. Allerdings können Sie von dort aus nicht noch einmal Enter-PSSession aufrufen um von dort aus weiter zum Zielserver zu gelangen.

Um dies dennoch auf sichere Art und Weise zu ermöglichen, müssen Sie eine Ressource based Kerberos contrained Delegation einrichten. Zugegeben böses „Wort“, auf Deutsch heißt das bestimmt noch viel schlimmer. Vergessen Sie das böse Wort und richten Sie es einfach ein. Dazu benötigen Sie auf Ihrem Client die RSAT-Tools und das Active-Directory Modul für PowerShell (oder, falls möglich, machen Sie es einfach am Domänencontroller). Der Rest ist auch nicht weiter schwer. Als Domain-Admin geben Sie auf Ihrem Client oder am Domänencontroller ein:

Set-ADComputer -Identity NameDesZielservers -PrincipalsAllowedToDelegateToAccount NameDesJumpHOsts

3.2.7.6   Konfiguration der Endpunkte (Anlaufstellen)

Endpunkte sind Schnittstellen, die Ihre Enter-PSSession oder Invoke-Command Anfragen auf dem entfernten Computer entgegen nehmen. Davon gibt es schon einige und die reagieren auch, sobald Sie Enable-PSRemoting mindestens einmal auf dem Zielsystem ausgeführt haben. Wie Sie sich mit einem bestimmten Endpunkt verbinden, um z.B. die PowerShell auf dem Zielcomputer in 32-Bit auszuführen, oder gar selbst eigene Schnittstellen (Endpunkte) anlegen, um z.B. nur einzelne Cmdlets zu erlauben, erfahren Sie in den nachfolgenden Abschnitten.

3.2.7.6.1    Endpunkte auflisten

Mit dem Cmdlet Get-PSSessionConfiguration können Sie sich einen Überblick der Remoting-Schnittstellen verschaffen:

Wenn Sie sich mittels Enter-PSSession auf diesen Rencher von einem anderen aus verbinden wöllten, würde dies nicht klappen. Denn hier steht in den Permission-Zeilen die Gruppe NT AUTHORITY\NETWORK Access Denied. Was soviel bedeutet wie, dass hier noch niemand Enable‑PSRemoting ausgeführt hat ;-). Fehlt dieser Eintrag und sind nur die Administratoren gelistet, klappt’s auch mit dem Verbindungsaufbau.

Wenn Sie keine weiteren Angaben machen, werden Sie natürlich mit dem „Standard“-Endpunkt microsoft.powershell verbunden, was im Normalfall der 64-Bit Version entspricht.

3.2.7.6.2    Mit einem Endpunkt Ihrer Wahl verbinden

Vielleicht brauchen Sie z.B. wegen 32-Bittiger Datenbanktreiber aber in der Remote-Konsole die 32‑Bit PowerShell. Dann können Sie sich auch geziel mit diesem EndPunkt verbinden. Beispiel:

Enter-PSSession –ComputerName Rechner1 –ConfigurationName microsoft.powershell32

Nun können Sie auch die 32 bittigen Datenbankzugriffe erfolgreich remote ausführen.

3.2.7.6.3    Eigene Endpunkte bauen

Um einen eigenen Endpunkt auf einem System einzurichten sind 2 Schritte nötig. Zuerst müssen Sie mit New-PSSessionConfigurationFile eine *.pssc-Konfigurationsdatei anlegen und dann mit deren Einstellungen, mittels Register-PSSessionConfiguration, den Endpunkt einrichten.

Stellen Sie sich vor Sie möchten einem Mitarbeiter der Personalabteilung ermöglichen einen neuen Benutzer mittels New-ADUser anzulegen. Auf seinem Client hat er normaler Weise weder das Active-Directory PowerShell Modul, noch die entsprechenden Berechtigungen bzw. Mitgliedschaft bei den Domänen Administratoren. Hier könnte es spannend sein einen entsprechenden Endpunkt anzulegen, der genau das ermöglicht.

3.2.7.6.3.1        New-PSSessionConfigurationFile

Die Konfigurationsdatei könnte so erstellt werden:

New-PSSessionConfigurationFile –ModulesToImport ActiveDirectory `
–VisibleCmdlets “New-AD
User“ –LanguageMode NoLanguage `
–SessionType RestrictedRemoteServer
 –Path C:\NewUser.pssc

-ModulesToImport ActiveDirectory legt fest, dass bei Verbindungsaufnahme das PowerShell ActiveDirectory Modul mit all seinen AD Cmdlets geladen wird. Alle auf dem Zielsystem verfügbaren Module können dementsprechend geladen werden.

-SessionType RestrictedRemoteServer sorgt dafür, dass nur die folgenden Cmdlets zur Verfügung stehen: Exit-PSSession, Get-Command, Get-FormatData, Get-Help, Measure-Object, Out-Default, und Select-Object. Alle anderen Cmdlets sind zunächst nicht verfügbar. Der Mitarbeiter der Personalabteilung kann sich also eigentlich nur verbinden und die Verbindung wieder abbauen. Also selbst die Cmdlets aus dem geladenen AD-Modul stehen nicht zur Verfügung. Wird der Parameter nicht angegeben, stehen die Cmdlets aus Microsoft.PowerShell.Core (also die Standard Cmdlets ohne weitere Module) zur Verfügung. Allerdings kann der Benutzer dann mit Import-Module auf dem Server verfügbare Module nachladen und die Cmdlets daraus auch benutzen.

-LanguageMode NoLanguage könnten Sie sich eigentlich sparen, da dies die Standardeinstellung ist, wenn man beim –SessionType RestrictedRemoteServer angibt. NoLanguage hat zur Folge, dass man keine Skriptblöcke, Variable oder Operatoren verwenden kann. FullLanguage würde alle PowerShell Fähigkeiten zur Verfügung stellen.

-VisisbleCmdlets New-ADUserist der eigentliche Clou an der Geschichte. Damit kann eine Whitelist definiert werden, welche Cmdlets außer denen zum Verbindungsabbau zulässig sind. In diesem Fall soll es nur New-ADUser sein. Sie können aber auch in einer Komma getrennten Liste weitere Cmdlets angeben. New-ADUser funktioniert natürlich nur, wenn auch das PowerShell Modul für Active-Directory (dies haben Sie ja bei –ModulesToImport angegeben) geladen ist. Das Quoten (“…“)ist wegen der – Zeichen in den Cmdlets hier natürlich äußerst hilfreich.

–Path C:\NewUser.pssc gibt letztlich an, in welche Datei die Konfiguration geschrieben werden soll.

Es gibt wie üblich noch einen ganzen Sack voll mehr Parameter, die die ExecutionPolicy, Umgebungsvariablen und vieles andere mehr festlegen. Mehr dazu verrät natürlich wieder Get‑Help.

Erklärungsbedürftig ist noch der Schalter –FunctionDefinitions. Darüber können Sie in die Konsole zusätzliche Funktionen mit einbauen. Allerdings ist die Syntax nicht ganz so, wie Sie es von normalen Functions her gewohnt sind. Die Funktion ist als Hash-Wert zu definieren. Dies setzt sich aus 3 Teilen zusammen: Name, Scriptblock und Options. Wenn Sie z.B. die Funktion .. (bekannt von SUSE-Linux oder Novell-Servern um ein Verzeichnis im Dateisystem noch oben zu gehen) implementieren möchten müssten Sie das so schreiben:

–FunctionDefinitions @{Name=“..“;Scriptblock={cd ..}}

Das waren jetzt aber nur 2 Teile…Ja, die Options sind, wie der Name schon sagt optional. Details dazu finden Sie in der Hilfe.

Name legt den Namen der Function fest und mit Skriptblock definieren Sie den Code der ausgeführt werden soll.

3.2.7.6.3.2        Register-PSSessionConfiguration

Schritt 1 ist damit durch. Nun müssen Sie die eben erzeugte Konfigurationsdatei im 2. Schritt mit Register-PSSessionConfiguration registrieren:

Register-PSSessionConfiguration –Name Personalabteilung `
–RunAsCredential Domäne
\AdminBenutzer –ShowSecurityDescriptorUI `
–Path
 C:\NewUser.pssc

Jegliche Rückfragen sind natürlich zu bejaen.

-Name Personalabteilung legt den Namen der Schnittstelle fest, den der Mitarbeiter später beim Verbindungsaufbau angeben muss. Beipiel:
Enter-PSSession –Computer Rechner1 –ConfigurationName Personalabteilung

–RunAsCredential Domäne\AdminBenutzer gibt an, dass der Benutzer in der Remotesitzung nicht mehr unter seinem auf dem Client angemeldeten Benutzer agiert, sondern mit dem hier definierten. User Mitarbeiter wird also innerhalb dieser Sitzung zum Domänen Administrator mit all den damit einhergehenden Berechtigungen. Er kann dann also auch Benutzer im Active-Directory anlegen, aber genauso gut auch löschen! Das einzige was Ihn von unbefugten Aktionen wie dem Löschen von Benutzern abhält ist die Tatsache, dass Sie in der Konfigurationsdatei nur New-ADUser, aber nicht z.B. Remove-ADUser zugelassen haben. Das Cmdlet steht ihm einfach nicht zur Verfügung. Sie sehen also wie extrem wichtig es ist, wenn man die ‑RunAsCredential Option nutzt, den Benutzer im Umfang der Cmdlets einzuschränken.

–ShowSecurityDescriptorUI legt fest, dass Sie mit Hilfe der grafischen Oberfläche bestimmen können, was welcher Benutzer in der Remotesitzung PowerShell seitig (nicht AD seitig) anstellen kann. Wenn Sie Ihrem Mitarbeiter der Personalabteilung also hier Berechtigungen einräumen, wird nur dieser sich auf den neu geschaffenen Endpunkt verbinden können. Da er an seinem Client Computer angemeldet, kein Administrator ist schlägt der Verbindungsaufbau zu den Standardendpunkten mit vollem Befehlssatz fehl. Er kann sich nur auf den von Ihnen erstellten Endpunkt verbinden, wird dort aber durch das –RunAsCredential zum Domänenadmin, der ausschließlich den Befehl New-ADUser einsetzen kann.

–Path C:\NewUser.pssc stellt die Verbindung zu der mit New‑PSSessionConfigurationFile erstellen Konfigurationsdatei her.

Auch für dieses Cmdlet gibt es natürlich wieder gefühlte 10 Mio. Schalter mehr, deren Beschreibung Sie gerne Get-Help entlocken können.

Möchten Sie einen Endpunkt nachträglich ändern möchten hilft Ihnen dabei Set‑PSSessionConfiguration und entfernen können Sie den Endpunkt natürlich auch wieder mittels Unregister-PSSessionConfiguration.

3.2.8   Kontrollfragen

Über welches Protokoll und welchen Port läuft die Kommunikation von WinRM?

Wie aktivieren Sie auf einem einzelnen PC die Remotefunktion der PowerShell?

Wo können Sie feststellen, welche Rechner ausserhalb eines Active-Directory sich auf Ihr System remote verbinden dürfen?

Wie lautet das Cmdlet, um einen einzelnenBefehl, eine Verarbeitungskette oder  ein Skript einmalig auf einem anderen PC auszuführen?

Wenn Sie mehrere unterschiedliche Dinge auf einem anderen PC erledigen wollen, wie können Sie die Konsole auf einen anderen PC umschalten?

Wie können Sie eine Remotesitzung wieder beenden?

Sie müssen mehrere Informationen zum Verbindungsaufbau angeben. Wie machen Sie das am besten?

PowerShell 2.0: Remotezugriff

3.2.9   Jobs

Wie Sie mit Jobs ein einfaches Multitasking, sprich parallele Verarbeitung von Aufgaben, durchführen können ist in diesem Kapitel zu lesen.

Einige Cmdlets wie z.B. Get-WMIObject verfügen über den Schalter –AsJob. Damit wird dieses Cmdlet als Job oder zu Deutsch als Aufgabe im Hintergrund abgearbeitet. Die Ausgabe erfolgt dann nicht auf dem Bildschirm. Der Vorteil dabei ist, dass die Kommandozeile sofort wieder freigegeben wird und Sie nicht auf das Ergebnis warten müssen. Stellt sich nur die Frage: Wo bleibt dann das Ergebnis?

Mit dem Cmdlet Get-Job können Sie sich eine Liste bereits vergebener Jobs erstellen was. Im Moment dürfte da noch nichts weiter zurückgegeben werden, da Sie noch keinen Job erstellt haben.

Wie gesagt sind einige Cmdlets mit einem speziellen Schalter ausgestattet um als Job abzulaufen. Sie können jedoch auch ganze Skripte oder Befehlsketten mit Cmdlets, die nicht über einen solchen Schalter verfügen als Hintergrundaufgabe laufen lassen, mithilfe des Cmdlets Start-Job.

Start-Job {sleep 60;ls c:\}

Diese Zeile hätte zur Folge, dass alles was in den gescheiften Klammern steht, im Hintergrund in einem weiteren PowerShell-Prozess ausgeführt wird. In diesem Falle, um etwas Zeit tot zu schlagen, haben Sie hier den Alias Sleep verwendet, der auf das Cmdlet Start-Sleep deutet und 60 Sekunden lang einfach gar nichts tut. Durch das Semikolon wird nach dem ersten Befehl ein weiterer ausgeführt, der Ihnen den Verzeichnisinhalt von Laufwerk C: anzeigt. Dadurch, dass der Job in einer separaten Konsole läuft vererben sich auch keine Variablen in die Jobs. Sie haben also dasselbe Problem wie bei einer RemoteShell. Aber auch hier gibt es wie beim Invoke-Command einen Schalter –ArgumentList mit dem Sie Variablen Inhalte an Jobs übergeben können. Innerhalb des Jobs holen Sie diese dann über den Array $args ab (siehe auch: Cmdlets oder Skripte auf mehreren Rechnern ausführen lassen).

Mit Get-Job können Sie sich nun jederzeit informieren, ob die Aufgabe inzwischen fertig ist. In der Spalte State sollte für eine Minute lang Running stehen und dann zu Completed wechseln. Wenn Sie sehen, dass der Status gewechselt hat, wissen Sie, dass der Job fertig ist. Die Spalte HasMoreDate sagt beim Wert True aus, dass Daten zur Abholung bereit stehen.

Wenn Sie sich nun die Daten abholen möchten, können Sie das mit dem Cmdlet Receive-Job tun:

Receive-Job 1

In der ersten Spalte der Liste von Get-Job wird die Jobnummer angezeigt und in der zweiten Spalte der Job Name. Diese beiden Werte werden automatisch vergeben, es sei denn Sie geben durch zusätzliche Schalter beim Cmdlet Start-Job eine andere ID oder Namen vor. Das Cmdlet Receive-Job 1 holt daher die Ergebnisse des Jobs mit der ID 1 ab und zeigt diese auf der Standardausgabe (dem Bildschirm) an. Wenn Sie nun noch einmal Get-Job ausführen sehen Sie in der Spalte HasMoreDate hat sich der Status auf False geändert. Wenn Sie also einmal die Daten abgeholt haben sind diese nicht mehr im Job enthalten. Wenn Sie nun erneut versuchen Receive‑Job 1 auszuführen, wird nichts weiter passieren, da der Job eben keine Daten mehr hat. Der Job ist nun nutzlos geworden und kann daher gelöscht werden. Dazu können Sie das Cmdlet Remove-Job verwenden:

Remove-Job 1

Ein Weiteres Get-Job zeigt Ihnen, dass der Job nun nicht mehr in der Liste geführt wird.

Die Jobs laufen nur so lange im Hintergrund weiter, solange die Konsole von der Sie gestartet wurden läuft. Wenn Sie also einen Job starten und die Konsole schliessen, dann ist damit auch der Job und das Ergebnis davon weg!

Wenn Sie die Ergebnisse aus einem Job mehrfach abrufen möchten können Sie das auch, indem Sie beim Abholen des Jobs den Schalter –Keep mit angeben:

Receive-Job 1 –Keep

Wenn Sie vorher eine neue Konsole gestartet und einen neuen Job mit der ID 1 erstellt haben, können Sie die Daten mit dem eben angebebenen Beispiel beliebig oft abrufen. Solange bis Sie einmal den Schalter –keep weglassen.

Anstatt in einer Endlosschleife immer wieder den Jobstatus abzufragen, wann er denn endlich fertig ist, sollten Sie lieber das Cmdlet Wait-Job verwenden, da es weniger Rechnenaufwand für den PC darstellt. Starten Sie doch noch einmal einen Job wie oben und dann lassen Sie die Shell einfach so lange warten bis Job Nr. 1 (oder welche Nummer Sie inzwischen auch immer haben mögen) fertig ist:

Wait-Job 1

Wenn Sie möchten, starten Sie dabei den Taskmanager und schauen Sie doch einmal auf die CPU-Last.

Wenn Sie mehrere Jobs gestartet haben und warten möchten bis alle fertig sind:

Get-Job | Wait-Job

Achtung! Leider sind Jobs nicht ganz so einfach zu nutzten, wie z.B. Funktionen. Wie Sie wissen vererben sich alle Funktionen und Variablen eines Hauptskripts automatisch in die Funktionen. Das gilt nicht für Jobs. Ein Job ist quasi wie eine separat gestartete PowerShell-Console zu verstehen. Alles was innerhalb des Jobs aus dem aufrufenden Skript im Job zur Verfügung haben möchten, müssen Sie mit Hilfe des Schalters –ArgumentList mitteilen bzw. übergeben. Eine Übergabe von Funktionen ist so z.B. nicht möglich. Die könnten Sie aber in ein separates Skript schreiben und dann mittels Dot-Sourcing sowohl im Hauptscript, als auch im Job einbinden. ;-)

3.2.9.1   Jobs und der Schalter –Credential

Mit –Credential (Get-Credential) können Sie den Inhalt des Jobs bequem unter einem anderen Benutzer ablaufen lassen. Leider gibt es da wohl einen Bug in der Form:

Lösung:

[environment]::CurrentDirectory='c:\temp'

Einfach direkt nach den Param() Block diese Zeile ^^ einbauen und alles wird gut ;-).

3.2.10                     Kontrollfragen

Mit welchem Cmdlet können Sie einen Befehl im Hintergrund ausführen lassen?

Wie können Sie feststellen welche Hintergrundjobs aktuell vorliegen?

Wie können Sie sich die Ausgabe eines Hintergrundjobs anzeigen lassen und wie sorgen Sie dafür, dass Sie das Ergebnis mehrfach abholen können?

Wie können Sie einen Hintergrundjob abbrechen?

Wie können Sie einen Hintergrundjob aus der Liste löschen?

PowerShell 2.0: Jobs

3.2.11                     Ereignisse

Mit sogenannten Events (Ereignissen) können Sie Ihr Skript über Dinge die woanders (ausserhalb Ihres Skriptes) passieren informieren lassen. Stellen Sie sich vor Sie möchten wissen, ob sich in einem bestimmten Verzeichnis auf Ihrer Festplatte etwas ändert. Dann müssten Sie ein Skript schreiben, dass in einer Endlosschleife, den Dateiinhalt abfragt und mit der Vorgängerversion vergleicht. Ich schätze, dass würde selbst einen CoreDuo ca. 50% Rechenzeit kosten. Effizienter ist das .NET damit zu beauftragen, dass wenn sich etwas in dem Verzeichnis zuckt, Ihrem Skript sofort ein Signal zu schicken. Das Überwachen solcher Signale funktioniert ohne Schleifemithilfe eines Jobs und braucht daher auch kein permanentes interpretieren des PowerShellSkriptCodes und somit keine Rechenleistung.Der Job wird automatisch gestartet, wenn das Ereignis stattfindet. Wie man diese Tricks einsetzt, erfahren Sie hier am Beispiel einer Prozessüberwachung.

Starten Sie notepad und tippen Sie danach die folgende Zeile:

$Tracker=ps | ? {$_.name -like "notepad"}

$Tracker erstellt ein Objekt vom Notepad Process. Wenn Sie mögen können Sie gerne einmal $Tracker an Get-Member mithilfe der Pipe übergeben und sich die Details zu diesem Objekt ansehen:

$Tracker | Get-Member

Sie werden feststellen, dass es dort nicht nur Methoden und Eigenschaften, sondern auch ein paar Einträge mit dem MemberType Event gibt. Diese Events (Disposed, ErrorDataReceived, Exited, OutputDataReceived) können überwacht werden.

$Aktion={Start-Process wordpad}

In der Variablen $Aktion wird Skriptcode hinterlegt, der ausgeführt werden soll, wenn ein bestimmtes Ereignis eintritt. Hier steht einfach nur, dass ein Prozess Namens Wordpad gestartet werden soll.

Register-ObjectEvent -InputObject $Tracker -EventName Exited -Action $Aktion

Dann registrieren Sie die Objektüberwachung mittels des Cmdlet Register-ObjectEvent als Job. Wer überwacht werden soll, geben Sie mit dem Schalter -InputObject an. Das zu überwachende Objekt muss dazu über entsprechende Event MemberTypes verfügen. In diesem Falle verwenden Sie $Tracker mit dem Notepad Prozess Objekt. Mit dem Schalter -EventName geben Sie den MemberType des Ereignisses an. Hier soll die Aktion starten, wenn das Programm beendet wird, also überwachen Sie das Ereignis Exited (Event aus der Spalte MemberType). Der Schalter ‑Action legt noch fest was geschehen soll, wenn das Ereignis eintritt. Entweder schreiben Sie es in geschweiften Klammern direkt hinter den Schalter, oder wenn es etwas mehr Code ist, tragen Sie hier einfach eine Variable ein. Die Variable haben Sie hier bereits in Form von $Aktion vorbereitet. Tja, nun schliessen Sie doch einmal das Notepad und voilà Wordpad wird gestartet.

Wichtig zu wissen ist, dass an dieser Stelle eine separate Sitzung gestartet wird. Wenn Sie in dem Code nach dem Schalter –Action  Text ausgeben, hat das nichts mehr mit der PowerShell Konsole zu tun, an der Sie die Eingaben machen. Sie können allerdings mit dem Cmdlet Write-Host explizit in die Konsole schreiben lassen. Aber einfach nur einen Text in Anführungszeichen klappt nicht, da die separate Sitzung nicht mehr die PowerShell ist, landet die Ausgabe im Job. Sie wissen schon…Receive-Job aus dem vorangegangenen Kapitel…Wenn Sie möchten können Sie auch gleich eine Job Verfolgervariable anlegen:

$Job=Register-ObjectEvent -InputObject $Tracker -EventName Exited -Action $Aktion

Dann könnten Sie die Rückgabewerte Ihres “Aktionscodes” abholen indem Sie einfach tippen:

Receive-Job $Job

Wenn Sie auf das Eintreten eines Ereignisses warten möchten können Sie dies entsprechend mit:

Wait-Job $Job

Eine Beschreibung zu New-Event spare ich mir, da es nur rudimentär unterstützt wird. So ist es z.B. nicht möglich an ein eigens erstelltes PSCustomObject benutzerdefinierte Events anzuhängen.

Weil ich gerade noch einmal die Jobs erwähnt habe…wie wäre es mit einem sich selbst terminierenden Job?

 

Register-ObjectEvent $Job -EventName StateChanged -Action {

    if ($eventArgs.JobStateInfo.State -eq [System.Management.Automation.JobState]::Completed) {

     # Löscht den original Job

     $sender | Remove-Job -Force

     # und die Event Registration

     $eventSubscriber | Unregister-Event -Force

     $eventSubscriber.Action | Remove-Job -Force

    }

   }

 

3.2.12                     Transaktionen

Den Begriff Transaktion kennen Sie sicher von Ihrer Bank. Wenn man von Ihrem Konto abbucht ist das Geld erst einmal von Ihrem Konto weg und erst danach wird es auf dem Empfänger-Konto gutgeschrieben. Stellen Sie sich vor genau dazwischen stürzen die Bankcomputer ab.  Bei Ihnen ist das Geld weg, aber beim Empfänger noch nicht eingegangen. Das wäre doch echt ärgerlich! Die Bank arbeitet mit sogenannten Transaktionen. Das bedeutet ein Vorgang ist entweder in seiner Gesamtheit abgeschlossen, oder wird zurückgesetzt. Im eben beschrieben Falle würde der Betrag auf Ihrem Konto also wieder gut geschrieben werden und Sie könnten die Buchung erneut versuchen. Man spricht auch von Transaktionssicherheit. In PowerShell 2.0 werden leider nur Zugriffe auf die Registrierdatenbank transaktionssicher unterstützt. Selbst beim Dateisystem ist diese Funktionalität leider nicht verfügbar, ganz zu schweigen von kompletten Skriptabschnitten.

Sie können damit also Beispielsweise einen, oder mehrere Registrierschlüssel anlegen und darin entpsrechende Einträge. Wenn das nicht nur 2 oder 3 sind, sondern richtig viele, dann kann das ein Weilchen dauern. Wenn Ihnen genau da mittendrin der PC abstürzt, kann das ziemlich häßlich werden. Wenn Sie die Zugriffe mit Transaktionen versehen, ist das halb so wild, weil alle „halb“ durchgeführten Änderungen zurück genommen werden und man wieder bei derselben Ausgangssituation erneut versuchen kann die Einträge zu schreiben.

$Key="HKCU:\Software\TransactionTest"

New-Item $Key

1..500 | Foreach {New-ItemProperty $Key -Name $_ -Value $_}

In der ersten Zeile des Skriptes wird in der Variablen $Key Der Pfad zu einem Schlüssel in der Registrierdatenbank hinterlegt. Wenn Sie mögen, starten Sie das Programm Regedit und schauen Sie doch einmal nach. Der Schlüssel Software ist in jeder Registry vorhanden, jedoch nicht der Schlüssel TransactionTest darunter. Durch die zweite Zeile New-Item $Key wird der Schlüssel TransactionTest in der Registry erstellt. Die letzte Zeile generiert Zahlen von 1 bis 500 und gibt diese über eine Pipe an eine Foreach Schleife. Dadurch wird der Befehl in den geschweiften Klammern 500 Mal wiederholt. New-ItemProperty legt einen Wert im zuvor erstellten $Key mit dem Namen und Wert $_ an. $_ enthält bei jeden Durchlauf die entsprechende Zahl. Also zunächst eine 1, dann eine 2, dann 3 usw. bis 500. Somit werden also 500 durchnummerierte Einträge in den Registrierschlüssel geschrieben. Das Problem bei diesem Skript liegt darin, dass wenn etwas schief geht ein Haufen Fehler produziert werden, wenn man versucht es noch einmal zu starten. Lassen Sie es einmal komplett durchlaufen. Wenn Sie dabei merken, dass es auf Ihrem Rechner zu schnell, oder zu langsam geht, können Sie für die weiteren Versuche einfach die 500 gegen z.B. 50 oder 5000 austauschen. Dementsprechend werden mehr oder weniger Einträge geschrieben. Wenn das Skript komplett durchgelaufen ist, öffnen Sie regedit, gehen Sie in HKEY_CURRENT_USER, klappen Sie den Eintrag Software auf und suchen Sie nach dem Eintrag TransactionTest. Schauen Sie einmal hinein und löschen Sie anschliessend den Schlüssel TransactionTest über das Programm regedit.

Jetzt starten Sie das Skript noch einmal und brechen Sie es mitten drin einfach mit Strg+C ab. Damit simulieren wir einen Absturz oder Fehler. Starten Sie ein weiteres Mal. Ist häßlich, oder? Das Skript versucht nun Einträge anzulegen, die es bereits gibt und es hagelt Fehlermeldungen. Bei anderen Registriereinträgen könnten natürlich noch weitaus schlimmere Sachen passieren, als ein paar rote Fehlermeldungen in einem PowerShellSkript.

Nun stellen Sie das Ganze auf Transaktionen um:

$Key="HKCU:\Software\TransactionTest"

Start-Transaction -Timeout 2

New-Item $Key -UseTransaction

1..500 | Foreach {New-ItemProperty $Key -Name $_ -Value $_ -UseTransaction}

Complete-Transaction

Die erste Zeile bleibt unverändert. In der zweiten Zeile geben Sie an, dass Sie von hierab entweder alle Einträge schreiben, oder alles wieder zurückgesetzt wird, wenn etwas schief läuft. Der Schalter ‑Timeout 2 sogar dafür, dass die nachfolgenden Aktionen innerhalb zwei Minuten abgeschlossen sein müssen, ansonsten wird auch wieder alles zurückgesetzt. Das ist hilfreich wenn das Skript sich irgendwie tot läuft. Die dritte Zeile kennen Sie bereits. Allerdings ist hier noch ein zusätzlicher Schalter –UseTransaction angegeben, der besagt, dass diese Aktion zur Transaktion dazugehört. Auch die vorletzte Zeile ist gleich, abgesehen wieder vom Schalter ‑UseTransaction. Das abschliessende Complete-Transaction am Ende des Skripts schaltet die gesamten Änderungen auf einmal „Live“ und beendet damit die Transaktion. Bitte beachten Sie, dass dies nur mit Registriereinträgen passiert. Das bedeutet, selbst wenn Sie dieselben Befehle verwenden, allerdings auf das Dateisystem, geht dies nicht! Nur die Registrierdatenbank unterstützt derzeit Transaktionen.

Probieren Sie das Skript aus, indem Sie wieder mittendrin einfach Strg+C drücken. Mit Get‑Transaction sehen Sie, dass die Transaction beim Status RolledBack anzeigt. Das bedeutet, dass die Änderungen zurück genommen wurden. Sie können das Skript nun einfach erneut laufen lassen und zwar ohne Fehlermeldungen!

Wenn Sie möchten können Sie die Transaktion auch manuell zurücksetzen lassen, durch dass Cmdlet Undo-Transaction. Auch zu diesen Cmdlets gibt es natürlich wieder einige zusätzliche Schalter die es zu erforschen gilt, wenn Sie das Verhalten noch anpassen möchten.

3.3     PowerShell 3.0

Hier werden die Neuerungen der Powershell 3.0 erklärt.

3.3.1   Hilfe

Die Hilfe von PowerShell 3.0 hat leider wieder einige Nachteile mit sich gebracht. Denn Sie funktioniert erst einmal nur halb, weil der Rest im Internet liegt. Damit dies annähernd an die Qualität von Verison 2.0 heranreicht, benötigen Sie also eine Internetverbindung. Viel Spaß wenn Sie mal eben schnell die Hilfe auf einem Server in einem abgeschotteten Netzwerk lesen wollen.

Natürlich funktioniert hier auch die Ausführung der Cmdlets genauso wie in Version 2.0. Nur leider bekommen Sie nur die Hälfe an Informationen, nämlich das was aus der Programmierung hervorgeht. Das wertvollste, die Examples (Beispiele), können Sie also schon einmal vergessen.

Die erweiterten Hilfetexte wurden angeblich aus Gründen der Aktualität ins Internet verfrachtet. Leider hat man dies bislang nur für die Englischsprachige Version der PowerShell gemacht. Andere Sprachen fehlen komplett, sprich es gibt gar keine erweiterte Hilfe, was natürlich gerade für den Einsteiger ganz übel ist.

3.3.1.1   Get-Help funktionstüchtig machen

Zunächst einmal kann sich die Online Hilfe in einer Englischen Version mit diesem Cmdlet herunterladen (Achtung UAC - muss mit erhöhten Rechten gestartet werden):

Update-Help

Vielleicht wird das auch irgendwann einmal in der Deutschen Fassung klappen. So lange können Sie sich mit einem billigen Trick weiterhelfen. Wenn Sie irgendwo eine Englische Version „herumliegen“ haben, kopieren Sie sich einfach die Inhalte von

C:\Windows\System32\WindowsPowerShell\v1.0\en-US
nach
C:\Windows\System32\WindowsPowerShell\v1.0\de-DE

Dann können Sie zumindest die Englische Hilfe lesen. Haben Sie keine Englische Version greifbar, können Sie auch die Englische Hilfe in der Deutschen Version herunterladen mittels:

Update-Help –UICulture en-us

Damit bekommen Sie das Unterverzeichnis en-US wie oben beschrieben und kopieren sich einfach wieder die Inhalte von en-US nach de-DE.

Alternativ können Sie mit Save-Help auch direkt in de-DE die Englische Version herunterladen:

Save-Help –UICulture en-us –DestinationPath “C:\Windows\System32\
WindowsPowerShell\v1.0\de-DE

Auf die Schnelle mal Infos zu einem einzelnen Befehl bekommen Sie mit:

Get-Help Get-Childitem –Online

Daraufhin öffnet sich Ihr standard Webbrowser und stellt den Inhalt, den Sie auch mit dem Schalter ‑full bekommen würden auf Englisch im Browser dar.

3.3.2   Show-Command

Dieses Cmdlet öffnet ein grafisches Fenster mit allen Parametern des Cmdlet, aber auch den CommonParameters. Dann kann man sich mit der Maus seinen Befehl zusammen klicken. Nicht wirklich spannend, aber vielleicht für einen Einsteiger hilfreich:

Show-Command Get-ChildItem

3.3.3   Vereinfachte Schreibweisen

Achtung! Wenn Sie diese Schreibweisen verwenden, sind Ihre Skripte nicht mehr abwärtskompatibel und können nur noch in Version 3.0 ausgeführt werden. Des Weiteren ist noch zu erwähnen, dass dies nur bei einfachen Abfragen zuverlässig funktioniert. Sobald Sie mit mehreren Vergleichs-Operatoren hantieren, empfehle ich aus Sicherheitsgründen die Verwendung der auf den alten Versionen bekannten Schreibweisen. In Skripten rate ich komplett von der Verkürzung ab.

3.3.3.1   Where-Object

Statt

Where-object {$_.Eigenschaft –Vergleichsoperator “Begriff“}

können Sie ja bekannter Maßen auch

? {$_.Eigenschaft –Vergleichsoperator “Begriff“}

schreiben. In PowerShell 3.0 können Sie das noch toppen mit:

? Eigenschaft –Vergleichsoperator “Begriff

Die geschweiften Klammer und das $_. können also entfallen. Praxisbeispiel:

ls c:\windows | ? length –gt 1000

Die Sache hat aber auch einen kleinen Haken. Sie können immer nur einen einfachen Vergleich machen. Mehrere logisch miteinander verknüpfte Vergleiche wie mit –and bzw. –or funktionieren nicht. So etwas schlägt also fehlt:

ls c:\windows | ? length –gt 1000 –and length –lt 2000

Das müssen Sie also nach wie vor ausschreiben:

ls c:\windows | ? {$_.length –gt 1000 –and $_.length –lt 2000}

3.3.4   Neue Systemvariable

Auch ganz toll: Statt $_ dürfen Sie nun auch $PSItem schreiben. Das Ergebnis ist dasselbe, nur ist die Schreibweise $PSItem natürlich inkompatibel mit Vorgängerversionen. Also vergessen sie den Quatsch am besten gleich wieder.

3.3.4.1   Foreach

Auch bei Foreach können Sie mit dem Alias % abkürzen. Wenn in einem Verzeichnis C:\test mindestens zwei Dateien ablegen, die Sie anschließen mit einer .NET-Methode löschen möchten, so lautet die Befehlskette:

ls c:\test | foreach {$_.delete()}

In Powershell 3.0 könnte das Ganze auch so ausschauen:

ls c:\test | % delete

Ähnlich wie bei Where-Object können hier die geschweiften Klammern und das $_. entfallen. Bei .NET-Methodenaufrufen sogar noch die runden Klammern.

3.3.5   Workflows

Ein Workflow ermöglicht Aufgaben quasi „gleichzeitig“ ablaufen zu lassen. Der Grunsätzliche Aufbau von Workflows ähnlet dem von Funktionen. Allerdings können Sie innerhalb eines Workflows steuern was nacheinander und was „zeitgleich“ ablaufen kann:

Workflow NameDesWorkflow {

 Parallel {

  Get-wmiObject –Class Win32_Operatingsystem

  Get-Service | select –first 3

  Get-Process | select –first 3

 }

}

Wenn Sie innerhalb eines Parallel Blocks gewisse Befehle in einer bestimmten Reihenfolge abarbeiten möchten, weil z.B. Das Ergebnis eines vorangegangenen Befehls in einem nachfolgenden Befehl weiterverarbeitet wird, können Sie diese Befehle noch einmal in einem Sequence {…Ihr Code…} Block einschliessen.

Das klingt nun alles wunderbar nach Multithreading und Performancesteigerung, aber Achtung! Geben Sie den oberen Abschnitt doch noch einmal als Funktion ein:

Function NameDerFunktion {

 Get-wmiObject –Class Win32_Operatingsystem

 Get-Service | select –first 3

 Get-Process | select –first 3

}

Dann führen Sie einmal beide in ein Measure-Command eingewickelt aus:

Measure-Command {NameDesWorkflow}

Measure-Command {NameDerFunktion}

Na, überrascht? Der Aufbau der Umgebung für diese Form der Verarbeitung schluckt auch einiges an Rechenzeit, daher sollten Sie sich gut überlegen, wo der Einsatz wirklich mehr Leistung bringt. Hier ein Skript, bei dem der Einsatz von Workflows tatsächlich einen ordentlichen Geschwindigkeitsschub bringt:

10  # quick subnetcheck for PowerShell 3.0 by Martin Lehmann

11  # Example:

12  # .\subnetchk.ps1 10.2.182.0/23

13  # lasts not even 4 Min. with 3/4 not reachable (offline)!

14  param (

15   [parameter(Mandatory=$true)]

16   [string]

17   [ValidatePattern("10\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]\.[1-2]?[0-9]?[0-9]/[0-9]?[0-9]")]

18   $subnet

19  )

20  workflow CheckNet {

21   param ([String]$Net,[byte]$Okt,[byte[]]$c)

22   if ($Okt -eq 4) {

23    foreach -parallel ($i in $c) {

24     inlinescript {

25      $Obj=New-Object -typename pscustomobject

26      $Obj | Add-Member -MemberType NoteProperty -Name "IP" -Value "$($Using:Net).$($Using:i)"

27      try {

28       Test-Connection -computer "$($Using:Net).$($Using:i)" -count 1 -ea Stop > $null

29       $Name=(ping -n 1 -a "$($Using:Net).$($Using:i)")[1].split(" ")[1]

30       $Obj | Add-Member -MemberType NoteProperty -Name "Status" -Value "available"

31       $Obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "$Name"

32      }

33      catch {

34       $Obj | Add-Member -MemberType NoteProperty -Name "Status" -Value "not available"

35       $Obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "N/A"

36      }

37      $Obj

38     }

39    }

40   }

41   if ($Okt -eq 3) {

42    foreach ($i in $c) {

43     foreach -parallel ($i2 in 0..255) {

44      if (!(($i2 -eq 0) -and ($i -eq $c[0])) -and !(($i2 -eq 255) -and ($i -eq $c[-1]))) {

45       inlinescript {

46        $Obj=New-Object -typename pscustomobject

47        $Obj | Add-Member -MemberType NoteProperty -Name "IP" -Value "$($Using:Net).$($Using:i).$($Using:i2)"

48         try {

49          Test-Connection -computer "$($Using:Net).$($Using:i).$($Using:i2)" -count 1 -ea Stop > $null

50          $Obj | Add-Member -MemberType NoteProperty -Name "Status" -Value "available"

51          $Name=(ping -n 1 -a "$($Using:Net).$($Using:i).$($Using:i2)")[1].split(" ")[1]

52          $Obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "$Name"

53         }

54         catch {

55          $Obj | Add-Member -MemberType NoteProperty -Name "Status" -Value "not available"

56          $Obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value "N/A"

57         }

58         $Obj

59        }

60       }

61      }

62     }

63    }

64   }

65   [byte]$mask=$subnet.split("/")[1]

66   $net=$subnet.split("/")[0]

 

67   if ($mask -lt 16) {throw "Subnetmask < 16 currently not supported!"}

68   if ($mask -gt 32) {throw "Subnetmask > 32? IPv6 currently not supported!"}

69   if ($mask -ge 24) {

70   $hosts=[math]::pow(2,(32-$mask))

71   $nets=[math]::pow(2,($mask-24))

72   $GoodNet=$false

73   $start=$net.split(".")[3]

74   for ($i=0; $i -lt $nets;$i++) {

75    if ($start -eq ($hosts*$i)) {

76     $GoodNet=$true

77    }

78   }

79   if (!$Goodnet) {throw "Wrong Subnetmask"}

80   Checknet $Net.substring(0,$Net.lastindexof(".")) 4 (([byte]$start+1)..([byte]$start+$hosts-2)) | select IP,Status,ComputerName | sort @{Expression="Status";Descending=$false},@{Expression="IP";Descending=$false}

81   }

82   if ($mask -lt 24 -and $mask -ge 16) {

83   $loops=[math]::pow(2,(24-$mask))

84   $nets=[math]::pow(2,($mask-16))

85   $GoodNet=$false

86   $start=$net.split(".")[2]

87   for ($i=0; $i -lt $nets;$i++) {

88    if ($start -eq ($loops*$i)) {

89     $GoodNet=$true

90    }

91   }

92   if (!$Goodnet) {throw "Wrong Subnetmask"}

93   $Net=$Net.substring(0,$Net.lastindexof("."))

94   Checknet $Net.substring(0,$Net.lastindexof(".")) 3 (([byte]$start)..([byte]$start+$loops-1)) | select IP,Status,ComputerName | sort @{Expression="Status";Descending=$false},@{Expression="IP";Descending=$false}

95  }

Das Skript pingt ein komplettes Subnetz durch. Da hier auf Timeouts gewartet werden muss, ist dies bei parallelem anstarten der Verbindungsversuche effizienter, als für jede einzelne Station auf Rückmeldung zu warten, bevor man die nächste Station angeht. Wenn Sie wollen, schreiben Sie das oben angegebene Skript doch einmal zu einer Funktion um. Nehmen Sie aber ein kleines Subnetz (z.B.: /28) zum testen!

Hierbei lernen Sie auch gleich noch ein paar Eigenheiten von Workflows kennen:

1.       Sie dürfen keine Aliase verwenden.

2.       Alle Parameter müssen mit ausgeschriebenem Bezeichner an ein Cmdlet übergeben werden.

3.       In Workflows werden ein paar spezial Formen von Cmdlets unterstützt, wie z.B.: Foreach –Parallel. Bei Foreach –Parallel wird die Schleife nicht nacheinander, sondern parallel abgearbeitet. Bei eine /24 Netzwerk, werden also alle 254 Stationen mehr oder weniger zeitgleich versucht zu erreichen, nicht nacheinander!

4.       Nicht alle Cmdlets und Befehlsabläufe sind Workflow tauglich. So z.B. ein Umleitungsoperator führt zu dieser Fehlermeldung:
Diese Probleme können Sie durch einschliessen der Anweisung in einen InlineScript {} Block umgehen. Leider stehen in diesem Block aber die zuvor gesetzten Variablen nicht zur Verfügung. Doch auch hier gibt es einen Workaround. Haben Sie beispielsweise eine Variable $a=10 vor dem InlineSkript {} Block gesetzt, können Sie innerhalb des SkriptBlock durch Ergänzung des Zauberwortes Using: doch darauf zugreifen. Beispiel:
$a=10
InlineSkript {
 $Using:a=$Using:a+10
}
$a

3.3.6   Scheduled Jobs

Scheduled Jobs unterscheiden sich von den „normalen“ Jobs die mit PowerShell 2.0 eingeführt wurden (Kapitel: PowerShell 2.0, Abschnitt: Jobs) dadurch, dass Sie nicht im RAM der aktuellen PowerShell Sitzung gehalten werden, sondern auf der Festplatte hinterlegt werden. Das hat den Vorteil, dass Sie also beruhigt die aktuelle Konsole schließen können, ohne dass dadurch auch der Job abgebrochen wird. Sie können sogar den Computer rebooten.

3.3.6.1   Job Trigger festlegen

Zunächst sollten Sie sich Gedanken machen zu welchem Zeitpunkt ein Skript ausgeführt werden soll. Diesen Zeitpunkt können Sie mithilfe des Cmdlets New-JobTrigger festlegen. Zur Verfügung stehen folgende Parameter und damit entsprechende Zeitdefinitionen:

-Once
Einmalig

-Daily
Täglich

-Weekly
Wöchentlich

-AtLogOn
Beim Anmelden eines Benutzers

-AtStartup
Wenn der PC bootet

Lieder gibt es weder einen Trigger(Auslöser) für Abmelden noch Herunterfahren.

Um einen ganz einfachen Trigger für die tägliche Aktivierung um 9:00 Uhr zu erstellen und in einer Variablen zu hinterlegen, geben Sie bitte folgendes ein:

$timetrigger=New-JobTrigger -Daily -At "9:00 AM"

Selbstverständlich dürfen Sie Ihrer Neugier auf hier wieder freien Lauf lassen:

$timetrigger | select *

Mehr über die unterschiedlichen Möglichkeiten einen Trigger zu definieren verrät Ihnen wieder:

help New-JobTrigger –full

3.3.6.2   Scheduled Job anlegen

Einen Auslöser haben Sie im vorangegangenen Abschnitt erstellt ($timetrigger). Nun können Sie eine Aktion mit diesem Auslöser durch das Cmdlet Register-ScheduledJob verknüpfen. Dies können ein paar simple PowerShell Cmdlets (z.B.: Get-Process) sein:

Register-ScheduledJob -Name ScheduledJob -ScriptBlock {Get-Process} -Trigger $timetrigger

…oder aber auch ein ganzes Skript:

Register-ScheduledJob -Name ScheduledJob -FilePath C:\IhrSkript.ps1 -Trigger $timetrigger

Mit dem Schalter –ArgumentList können Sie auch gerne wieder Daten an den Job übergeben.

Durch den Schalter -ScheduledJobOption können Sie weitere Rahmenbedingungen festlegen unter denen der Job ausgeführt wird. Entweder geben Sie direkt nach dem Schalter die Parameter in Form eines Hash an:

@{WakeToRun; StartIfNotIdle}

…oder übergeben eine Variable, die Sie mit dem Cmdlet New-ScheduledJobOption definieren können. Welche Optionen alle zur Verfügung stehen verrät:

help New-ScheduledJobOption –full

Hat man den Job nicht über die ScheduledJobOptions „versteckt“ findet man Ihn im Taskscheduler (der Aufgabenplanung) wieder:

3.3.6.3   Einem Scheduled Job mehrere Trigger zuordnen

Selbst verständlich können Sie einem Scheduled Job auch mehrere Auslöser hinzufügen:

Add-JobTrigger -Trigger (New-JobTrigger -AtStartup) -Name NameDesBereitsExisitierendenJobs

3.3.6.4   Scheduled Jobs und Jobtrigger ändern und löschen

Selbstverständlich können Sie die angelegten Scheduled Jobs und deren Trigger auch nachträglich verändern oder löschen.

Zum Abrufen der Konfiguration stehen Ihnen diese Cmdlets zur Verfügung:

Get-JobTrigger

Get-ScheduledJob

Get-ScheduledJobOption

Zum abändern bieten sich diese Cmdlets an:

Set-JobTrigger

Set-ScheduledJob

Set-ScheduledJobOption

Um Scheduled Jobs oder deren Trigger zu pausieren:

Disable-JobTrigger

Disable-ScheduledJob

Enable-JobTrigger

Enable-ScheduledJob

Und letztendlich zum löschen:

Remove-JobTrigger

Unregister-ScheduledJob

Wenn Sie das Buch bis hierhin aufmerksam gelesen haben, erspare ich mir die Kommandos hier im Detail zu erläutern. Schließlich will ich Sie auch nicht langweilen. Lesen Sie ggf. noch einmal den Abschnitt Jobs im Kapitel PowerShell 2.0.

3.3.7   PSSession Disconnect

Ab Version 3.0 können Sie RemoteSitzungen (Grundlagen siehe Kapitel PowerShell 2.0 Abschnitt Remote Zugriff) auch vorübergehend trennen, um sich später wieder damit zu verbinden.

Mittels Get-PSSession Computername können Sie sich die auf dem angegebenen System existierenden Sitzungen anzeigen lassen und ob diese Sitzungen aktuell verbunden sind.

Disconnect-PSSession kann die Verbindung trennen, ob diese zu beenden. Beispiel in Kombination mit Get-PSSession:

Get-PSSession -ComputerName NameDesRemoteComputers -Name NameDerSitzung | Disconnect-PSSession

Ähnlich funktioniert die Wiederaufnahme des Kontaktes:

Connect-PSSession -ComputerName NameDesRemoteComputers -Name NameDerSitzung

3.3.8   New-PSDrive -Persist

New-PSDrive hat den zusätzlichen Schalter –Persist erhalten, mit dem man ähnlich wie NET USE nicht nur für die aktuelle Sitzung der Konsole eine Laufwerksverbindung herstellen kann, sondern für den gesamten Userkontext. Das bedeutet, das Laufwerk taucht dann z.B. auch im Windows Explorer auf. Aber es ist kein NET USE! Das merken Sie spätestens dann, wenn Sie über PowerShell Remoting arbeiten. Dort klappt das NET USE nämlich nach wie vor ohne jedes Murren. New-PSDrive –Persist hingegen beklagt weiterhin Zugriffsprobleme (wie in Abschnitt über UNC-Pfade in RemoteSession beschrieben).

3.3.9   Out-Gridview –Passthru

Out-Gridview fand ich als abschliessendes Cmdlet einer Pipe nie besonders nützlich. Durch den neuen Schalter –Passthru ist es aber eine sehr interresante Möglichkeit zur Eingrenzung von Werten aus einer Tabelle geworden.

$SelektierteProzesse=Get-Process | Out-GridView -PassThru
$SelektierteProzesse | Stop-Process

Dadurch brauchen Sie sich selbst nicht mehr um die Gestaltung einer komplexen GUI zu kümmern.

3.3.10                     Module in Version 3.0

Als zusätzliche Module sind PSWorkflow und PSScheduledJob hinzugekommen. Da Sie aber ab Version 3.0 Module nicht mehr laden müssen, um die Cmdlets zu verwenden erspare ich mir hier weitere Ausführungen und verweise auf die vorangegangenen Abschnitte in dem die Möglichkeiten beschrieben stehen.

3.3.11                     PowerShell Remoting

Auch beim PowerShell Remoting sind einige Erweiterungen hinzugekommen, die in diesem Kapitel beschrieben stehen.

3.3.11.1                Zusätzliche Remoting Cmdlets

Wie Sie wissen, können Sie eine Sitzung (mit einer Verbindungsvariable – siehe PowerShell 2.0, PowerShell Remote Zugriff im Abschnitt Die Kommandozeile auf einen entfernten Rechner umschalten) über das Cmdlet Exit-PSSession beenden. Dabei wird allerdings der aktuelle Prozess (die Konsole) auf dem entfernten Rechner beendet. Aller definierten Variablen sind damit natürlich auch weg. Wenn Sie sich erneut Verbinden müssen Sie diese wieder neu definieren. Mit Disconnect-PSSession können Sie die Verbindung trennen, ohne den Prozess auf dem RemoteSystem zu beenden und sich anschließend mit Reconnect-PSSession wieder auf diese Konsole drauf schalten. Dabei bleiben alle Variablen und deren Inhalte auf dem RemoteSystem auch über die Trennung der Verbindung hinweg erhalten. Das können Sie sich so ähnlich vorstellen, wie wenn Sie eine Remotedesktop-Verbindung trennen, anstatt sich abzumelden.

3.3.11.2                Web Access

Mit PowerShell Web Access haben Sie die Möglichkeit einen mit IIS ausgestatteten Windows Server 2008 oder höher als Gateway einzusetzen. Dieser ermöglicht Ihnen den PowerShell Zugriff mittels eines Webbrowsers (Cookies müssen zumindest für diese Website erlaubt werden).

3.3.11.2.1            Einfache Einrichtung von PowerShell Web Access

Führen Sie die nachfolgenden 4 Schritte zur Einrichtung von PowerShell Web Access aus:

1.       Installieren Sie auf einem Windows Server 2008 die Rolle Webserver(IIS) und das Feature Windows PowerShell Web Access (z.B. über PowerShell selbst: Install‑WindowsFeature Web‑Server, NET‑Framework‑45‑ASPNET, WindowsPowerShellWebAccess).

2.       Ersetzen Sie im folgenden Befehl den kursiv geschriebenen Teil, durch die Ihrem Webserver entsprechenden Bezeichnungen:
Install-PswaWebApplication –webApplicationName MeinePowerShell ‑WebsiteName www.IhreInternetAdresse.de
Wenn Sie kein Zertifikat für die SSL-Verschlüsselung haben, können Sie zu Testzwecken den Schalter –UseTestCertificate angeben. Dies sollte aber unter keinen Umständen bei Produktivsystemen gemacht werden. Unter anderem, weil dieses Zertifikat nach 90 Tagen ungültig wird. Achtung: -Websitename gibt nicht zwingend die URL an, sondern den Name unter dem Ihre Internetseite im IIS gelistet ist (z.B. auch DefaultWebsite).

3.       Nun müssen Sie noch festlegen, wer sich auf welche Ihrer Computer verbinden darf. Auf diesen Computern muss natürlich PowerShell Remoting aktiviert sein (Lesen Sie dazu ggf. das Kapitel PowerShell Remote Zugriff). Der Zugriff wird über sogenannte PswaAuthorizationRules gesteuert:
Add-PswaAuthorizationRule –ComputerName ZielComputerAufDemSieDieKonsoleFreigebenMöchten –UserName DomänenName\BenutzerDemSieDiesenZugriffErlaubenMöchten ‑ConfigurationName Microsoft.PowerShell
Auch hier sind die kursiven Bestandteile wieder entsprechend zu ersetzen. Mit
Get-PswaAuthorizationRule können Sie sich einen überblick über die erstellten Regeln geben lassen und mit Remove-PswaAuthorizationRule einzelne Regeln wieder löschen. Statt einzelne Benutzer anzugeben können Sie natürlich auch mit Gruppen aus Ihrem Active-Directory arbeiten:
Add-PswaAuthorizationRule –ComputerGroupName GruppeVonZielComputernAufDenenSieDieKonsolenFreigebenMöchten ‑UserGroupName DomänenName\BenutzerGruppeDerSieDiesenZugriffErlaubenMöchten ‑ConfigurationName Microsoft.PowerShell

4.       Das war’s auch schon. Ab sofort können Sie sich mit einem Browser auf den Webserver unter der entsprechenden URL verbinden:
https://www.IhreInternetAdresse.de/MeinePowerShell

Beim Benutzernamen bitte nicht vergessen, die Domäne voranzustellen. Bei Computernamen geben Sie an zu welchem Computer Sie sich weiterverbinden möchten. PowerShell Web Access wird also nicht auf dem Computer eingerichtet, auf dem Sie die Konsole bereitstellen möchten, sondern auf irgendeinem Computer mit installiertem IIS, der die Anforderung entsprechend weiter reicht. Sie können sich nur mit Computern verbinden, die vorher durch ein PswaAuthorization Rule (auf dem Webserver Gateway) erlaubt wurden und natürlich auch nur mit den in derselben Regel angegebenen Benutzern.

Mehr Möglichkeiten und Details zur Konfiguration finden Sie bei Microsoft im Internet unter:
http://technet.microsoft.com/de-de/library/hh831611.aspx

Weiter Informationen zu unterstützten Browsern und Verhalten der Weboberfläche wie Tastenbelegung (Stichwort: Strg+C) finden Sie unter:
http://technet.microsoft.com/de-de/library/hh831417.aspx

Tiefer möchte ich an dieser Stelle nicht gehen, da die PowerShell in meinen Augen viel zu mächtig ist, als Sie über das Internet bereit zu stellen. Diese Kapitel ist daher nur der Vollständigkeit halber enthalten.

3.3.12                     WMI vs. CIM

Wie im Abschnitt WMI Objekte ausführlich behandelt, können Sie mittels WMI auf DCOM zugreifen. In Version 3.0 hat WMI nun einen Bruder in Form von CIM erhalten.

Die Cmdlets funktionieren ähnlich, bieten an der einen oder anderen Stelle jedoch Unterschiede. So läßt sich beispielsweise:

Gwmi –list

durch:

Gcls

abkürzen. Gcls ist ein Alias für Get-CimClass.Dieser fügt bei Bedarf automatisch den –List Schalter ein.

Wenn es nach Microsoft geht ist WMI veraltet und wird in zukünftigen Windows Versionen möglicher Weise nicht mehr unterstützt, da angeblich die Firwallkonfiguration für RPC so schwierig ist. Da sind wir nun auch schon beim wesentlichen Unterschied! WMI verwendet RPC, während CIM über Windows-Remoting zugreift. Für Windows-Remoting müssen natürlich nicht nur die Ports in der Firewall auf sein, sondern auch noch PowerShell-Remoting auf dem Zielsystem aktiviert. Wo ist also der Konfigurationsaufwand größer und wo in installierte Basis von Systemen derzeit (2016) größer? WMI ist also nach wie vor die bessere Wahl.

CIM hat allerdings noch ein weiteres Feature, was sich sowohl als Vor-, als auch als Nachteil erweist. CIM kann Sitzungsbasiert arbeiten.

$Sitzung=New-CimSession –Computername Rechner1, Rechner2, Rechner3

Dies baut eine dauerhaft geöffnete Verbindung zu den 3 Rechnern auf.

Sie können auch die Cim-Cmdlets dazu „überreden“ RPC zu verwenden, indem Sie zusätzlich eine Protokolloption mit angeben:

$RPC=New-CimSessionOption -Protocol DCOM

$Sitzung=New-CimSession –Computername Rechner1 -SessionOption $RPC

Vorteil einer Sitzung: Man kann ohne erneuten Verbindungsaufbau schnell hintereinanderer mehrere Abfragen durchführen.

Get-CimInstance –CimSession $Sitzung Win32_LogicalDisk

Nachteil einer Sitzung: Die Verbindungen bleiben die ganze Zeit geöffnet, bis man die Verbindung(en) wieder abbaut.

Remove-CimSession $Sitzung

3.3.13                     Anti-Malware Scan Interface (AMSI)

AMSI ist eine Schnittstelle, die es Windows Defender und 3-Herstellern von Sicherheitssoftware ermöglichen soll schadhaften Code kurz vor der Ausführung zu entdecken und zu verhindern. Auch PowerShell wird ab Version 3.0 von AMSI durchleuchtet. Sollten Sie einmal Probleme haben, die Sie darauf zurückführen, versuchen Sie einfach die PowerShell im Kompatibilitätsmodus mit Version 2.0 auszuführen. Hier wurde AMSI noch nicht unterstützt und lässt daher jeglichen PowerShellCode zu. Aufruf:

PowerShell.exe -Version 2.0 -File IhrScript.ps1

Um für mehr Sicherheit zu sorgen, können Sie an Windows 8 / Server 2012 die Abwärtskompatibilität mit PowerShell 2.0 abschalten.

Wenn Sie PowerShell 3.0 und höher verwenden möchten, können Sie AMSI auch dediziert für PowerShell abschalten (allerdings poppt dann ein UAC-Dialog auf und weißt auf die „Missetat“ hin):

Set-MpPreference -DisableRealTimeMonitoring $true -DisableIOAVProtection $true

3.4     PowerShell 4.0

Hier werden die Neuerungen der Powershell 4.0 erklärt.

3.4.1   ISE Steuerung

3.4.1.1   Regionen – Bereiche zum auf und zuklappen selbst definieren

Mit der neuen Kommentar-Option region können Sie selbst bestimmen wo im ISE Bereiche die mit +/- auf- und zugeklappt werden können zur Verfügung stehen. Syntaktisches Beipiel:

#region BezeichnerIhrerWahl

Hier steht Ihr Code-Bereich der auf-/zugeklappt werden kann

#endregion

In ISE wird dann vor #region BezeichnerIhrerWahl ein – Zeichen eingeblendet. Wenn Sie darauf klicken verschwindet der komplette Bereich bis zum #endregion Tag aus der Ansicht und das Minus wandelt sich in ein + Zeichen. Nur noch die Zeile #region BezeichnerIhrerWahl ist zu sehen. Mit einem Klick auf + können Sie den Bereich natürlich wieder anzeigen lassen.

Das eignet sich besonders gut, um z. B. die kompletten Aufbau für die Hilfe im Funktionskopf ein-/ausblenden zu lassen.

#region Hilfetext

<#

.SYNOPSIS

 Zeigt alle Eigenschaften von Dateien und Verzeichnissen an.

.DESCRIPTION

 Der Befehl soll den Alias aus SuSE-Linux ll implementieren. Auf einem SuSE-Linux System ist ll ein Alias auf ls –l. ls –l wiederum macht ein sogenanntes List Long, sprich es werden alle möglichen Informationen zu Verzeichnissen und Dateien dargestellt. Hier in der PowerShell wird ein Get-Childitem auf das entsprechende Verzeichnis durchgeführt und anschlissend an ein Select-Object * übergeben, was alle Informationen zu den von Get-Childitem gelieferten Datei- und Verzeichnisobjekten darstellt.

.EXAMPLE

 ll c:\

 Listet das Wurzelverzeichnis von Laufwerk C: mit allen Detailangaben.

.EXAMPLE

 „c:\“ | ll

 Übergibt das Verzeichnis über eine Pipe an den Befehl ll.

.LINK

 www.martinlehmann.de

#>

#endregion

Function ll {

 Param(

  [Parameter(Mandatory=$true,ValuefromPipeline=$true)]

  [String[]]

  $Verzeichnis

 )

 Get-Childitem $Verzeichnis | Select *

}

3.4.1.2   ISE um eigene Funktionen erweitern

Sie können das Integrated Scripting Environment um eigene Funktionen erweitern. Alles was Sie dafür brauchen ist ein bisschen Forscherdrang und die Variable $psISE. Diese Variable steht Ihnen nur innerhalb der ISE selbst zur Verfügung, nicht in der normalen Konsole. Dort wäre sie ja auch nutzlos.

Die Variable lässt sich wie alle anderen mit Get-Member im unteren (blauen) Teil erforschen. Dabei könnten Sie u.a. auf das Unterobjekt $psISE.currentfile.Editor.SelectedText stoßen. Das enthält den Textbereich, den Sie im oberen Bereich markiert haben. $psISE.currentfile.Editor.Text enthält das komplette Skript. $psISE.currentfile.Editor.CaretLine enthält die aktuelle Cursor Zeile und $psISE.currentfile.Editor.CaretColumn die aktuelle Cursor Spalte. Beide zusammen ergeben die aktuelle Textcursorposition. Mit der Methode $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add() können Sie zusätzliche Einträge im Addon-Menüpunkt erstellen.

So können Sie mit dem folgenden Skript im Handumdrehen eine Funktion bauen die Ihnen die markierten Bereiche ein- oder ausrückt:

10.         Function Space {

11.          [Array]$All=$psISE.currentfile.Editor.Text.split(13)

12.          [Array]$Marked=$psISE.currentfile.Editor.SelectedText.split(13)

13.          $Markedcount=0

14.          $New=""

15.          $Zeile=$psISE.currentfile.Editor.CaretLine-1

16.          $Spalte=$psISE.currentfile.Editor.CaretColumn-1

17.          if ($Marked.count -eq 1) {

18.           if ($Insert) {

19.            $All[$Zeile]=" "+$All[$Zeile]

20.           } else {

21.            $All[$Zeile]=$All[$Zeile].substring(1)

22.           }

23.          } else {

24.           $Counter=0

25.           if ($All[$Zeile].substring($Spalte) -eq $Marked[0]) {

26.            $Marked | foreach {

27.             if ($Insert) {

28.              $All[$Zeile+$Counter]=" "+$All[$Zeile+$Counter]

29.             } else {

30.              $All[$Zeile+$Counter]=$All[$Zeile+$Counter].substring(1)

31.             }

32.           $Counter++

33.            }

34.           } else {

35.            $Marked | foreach {

36.             if ($Insert) {

37.              $All[$Zeile-$Counter]=" "+$All[$Zeile-$Counter]

38.             } else {

39.              $All[$Zeile-$Counter]=$All[$Zeile-$Counter].substring(1)

40.             }

41.             $Counter++

42.            }

43.           }

44.          }

45.          foreach ($Line in $All) {

46.           $New+=$Line+[char]13

47.          }

48.          $psISE.currentfile.Editor.Text=$New.substring(0,$New.length-1)

49.         }

50.        

50.$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Leerzeichen einfügen",{$Insert=$true;Space},"Alt+I") | out-null

51.         $psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Leerzeichen entfernen",{$Insert=$false;Space},"Alt+E") | out-null

Hier gibt es leider ein paar Gemeinheiten. Zunächst einmal müssen die den String-Text in einen Array umbauen. Dabei hilft die Methode .split(13). Diese sorgt dafür, dass das Carriage Return (13=Zeilenumbruch) zum Array-Trenner wird. Wenn nur eine Zeile markiert ist (Zeile 17), brauchen Sie nur die einzelne Zeile entweder rein-/rausrücken. Werden mehrere Zeilen markiert, muss zunächst rausgefunden werden, ob der Textcursor am oberen, oder unteren Ende der Markierung steht. Dafür dient der String-Vergleich in Zeile 25. Je nachdem müssen die Zeile von der Cursorposition auf- oder abwärts nach links bzw. rechts verschoben werden. Bei bauen des Arrays ging das Zeichen für die neue Zeile verloren, um das für jedes Zeile wieder am Ende einzufügen gibt es die Zeilen 45-47. In Zeile 48 wird dann in die Variable $psISE.currentfile.Editor.Text  der bearbeitete Text zurück geschrieben. Allerdings ohne das letzte Carriage  Return, denn sonst hätten Sie bei jedem Funktionsaufruf eine Zeile mehr an Ende des Skriptes. Die letzten beiden Zeilen enthalten dann die ergänzten Menüeinträge unter Add-Ons in der ISE mit dem jeweiligen Funktionsaufruf inkl. Tastatur-Shortcut. Damit dies dann jedes Mal beim Aufruf der ISE zur Verfügung steht und nicht jedes Mal manuell aufgerufen werden muss, nennen Sie die Datei Microsoft.PowerShellISE_profile.ps1 und legen sie unter C:\Users\Benutzername\Documents\WindowsPowerShell ab.

3.4.2   Vereinfachte Schreibweisen, auch in Workflows

Achtung! Wenn Sie diese Schreibweisen verwenden, sind Ihre Skripte nicht mehr abwärtskompatibel und können nur noch in Version 4.0 ausgeführt werden. Anstatt pipen können nachfolgende Befehle wie eine Methode aufgerufen werden. Beispiel:

(Get-Process).Where-Object({$_.Name -like "svc*"})

oder auch mit Aliasen:

(ps).where({$_.Name -like "svc*"})

Aber nicht übertreiben…

(ps).?({$_.Name -like "svc*"})

…denn die letzte Variante schlägt fehl. ;-)

In Workflows sind nun teilweise auch abgekürzte Parameter möglich.

3.4.3   Zusätzliche Cmdlets, Parameter

In diesem Abschnitt werden die neuen kleinen Bonbons der Version 4.0 aufgezählt.

3.4.3.1   Get-Process

Get-Process wurde um den Parameter –IncludeUserName erweitert. Da auch in den versteckten Eigenschaften der Benutzername in den älteren PowerShell-Versionen nicht enthalten ist, ist dies durchaus eine sinnvolle Erweiterung.

3.4.3.2   Scheduled Jobs

Die Cmdlets für die Scheduled Jobs sind erweitert worden. Allerdings nur, wenn Sie auch Windows 8/Windows Server 2012 oder höher einsetzen. Bei PowerShell 4.0 auf einem Windows Server 2008 z.B. fehlen diese Parameter nach wie vor.

Der Parameter –RunNow erlaubt nun auch einen Job sofort zu starten. Die Cmdlets New-JobTrigger und Set-JobTrigger haben den Parameter –RepeatIndefinitely erhalten, womit sich ein Job auf unbestimmte Zeit wiederholen lässt.

3.4.3.3   Workflows

Mit dem Cmdlet Suspend-Workflow können Sie (darf auch innerhalb eines Workflows eingesetzt werden) die Abarbeitung der Workflows anhalten. Die Workflows selbst werden als Job verwaltet. Das hat zur Folge, dass Sie mit Get-Job nicht nur Jobs, sondern auch aktuelle Workflows sehen. Haben Sie einen ausgesetzten Workflow in der Jobliste identifiziert, können Sie diesen mittels Resume-Job fortsetzen lassen.

Weiterhin können Sie nun innerhalb Workflows den Common-Parameter Schalter ‑PSPersist $true nach beliebigen Cmdlets angeben. Dies sorgt dafür, dass nach Ausführung des Cmdlet ein CheckPoint erstellt wird. Wird dann (Warum auch immer – z.B. Computer startet neu) der Workflow unterbrochen, kann dieser später an genau derselben Stelle fortgesetzt werden. Variableninhalte etc. werden im CheckPoint ebenfalls vermerkt .Natürlich kostet das Setzen eines jeden Checkpoint Leistung. Also wenn’s schnell gehen soll, setzen Sie den Schalter sparsam ein.

Beispiel:

Workflow Interuptus {

 Get-Service –PSPersist $true # Setzt den Checkpoint

 Suspend-Workflow # Workflow pausieren, nun können Sie rebooten

 Get-Process # Hier soll’s dann nach dem Reboot weiter gehen

}

Interuptus

Wenn Sie dies ausgeführt haben und Ihren Rechner neu starten, werden Sie hinterher über Get‑Job Diesen Workflow wieder finden. Mittels Resume-Job Nr. können Sie den Workflow fortsetzen und mit Receive-Job Nr. die Ausgabe der Prozessliste abholen.

Anstatt dem Common-Parameter –PSPersist können Sie auch das Cmdlet CheckPoint‑Workflow verwenden, um einen CheckPoint zu setzen. Geschmackssache! Ein CheckPoint-Workflow Cmdlet in einer eigenen Zeile ist vielleicht übersichtlicher. Wenn Sie sowieso laufend –PSPersist verwenden, können Sie auch gleich die Umgebungsvariable $PSPersistPreference auf $true setzen.

3.4.3.4   Test-NetConnection

Auch dieses Cmdlet steht Ihnen nur zur Verfügung, wenn Sie auch Windows 8/Windows Server 2012 oder höher einsetzen. Bei PowerShell 4.0 auf einem Windows Server 2008 z.B. fehlt dieses Cmdlet.

Dieses Cmdlet macht eigentlich das Gleiche wie das Test-Connection Cmdlet, kann allerdings noch etwas mehr Details liefern, warum eine Verbindung fehlschlägt.

3.4.3.5   FileHashes erstellen

Haben Sie eine unübersichtliche Fotosammlung, die Sie nach Dubletten durchforsten möchten?

Das Cmdlet Get-Filehash hilft Ihnen dabei durch generieren des Hashwertes einer Datei. Haben 2 Dateien denselben Hashwert, sind die beiden Dateien inhaltlich gleich. Über den Parameter ‑Algorithm könnte man noch den Algorithmus festlegen, aber der Standard SHA256 sollte für die meisten Aufgaben passen:

Get-FileHash C:\PfadUnd\NamederDateiVonDerDerHashGebildetWerden.Soll

3.4.3.6   Web Zugriffe, REST konform

Die beiden Cmdlets Invoke-RestMethod und Invoke-WebRequest gibt es zwar auch schon in Version 3.0, aber ab Version 4.0 funktionieren Sie auch ;-).

Mittels Invoke-WebRequest kann man recht einfach Inhalte per FTP oder HTTP(S) aus dem Internet abrufen. Z.B.:

Invoke-WebRequest www.martinlehmann.de

REST steht für Representational State Transfer und stellt eine Möglichkeit zur standardisierten Kommunikation auf http-Basis zwischen Computern dar. Eine tolle Erklärung darüber findet man unter: http://de.wikipedia.org/wiki/Representational_State_Transfer.

Um Bespielsweise den PowerShell RSS-Feed vom Microsoft auszulesen könnte man zunächst einmal mit:

Invoke-RestMethod blogs.msdn.com/powershell/rss.aspx | gm

schauen, was man hier alles “anstellen” kann. Mit

Invoke-RestMethod blogs.msdn.com/powershell/rss.aspx | select ` title, description

können Sie sich dann über die Inhalte her machen.

3.4.4   Desired State Configuration

Die DSC (Desired State Configuration) ermöglicht Systeme in einen gewünschten Zustand zu bringen. In erster Linie ist diese Funktion also für Rollouts interessant. Landläufig sagt man auch gerne „Druckbetankung“. Man könnte es auch ein bisschen mit Gruppenrichtlinien vergleichen. Auf allen Maschinen die Sie als Parameter übergeben, werden entsprechende Einstellungen bzw. Veränderungen vorgenommen.

Häßlich: Leider wird mit WMF 4.0 nur eine Basisausstattung mit der Grundfunktionalität geliefert. Weitere Ergänzungen, wie z.B. der Zugriff auf so grundlegende Dinge wie Active-Directory, werden in sogenannten Waves zur Verfügung gestellt. Diese kommen als *.zip Dateien daher und müssen ins Module Verzeichnis entpackt werden. Weiterhin sind die Erweiterungen noch als experimental gekennzeichnet. Mit Fehlfunktionen ist daher zu rechnen. Aktuell, da ich diese Zeilen schreibe, ist Wave 9.

Voraussetzungen:

·         Bei Windows 8.1 und Windows Server 2012 R2 muss KB2883200 installiert sein.

·         Bei Windows 7 und Windows Server 2008 muss das komplette .NET Framework 4.5 vor dem Windows Management Framework (WMF) mit PowerShell 4.0 installiert werden.

·         PowerShell Remoting muss auf den Zielsystemen aktiviert sein! Siehe Abschnitt PowerShell Remote Zugriff

Das in PowerShell 4.0 neu dazugekommene Zauberwort ist Configuration NameDerKonfiguration {Ihr Scriptcode}. Ähnlich wie in Funktionen oder Workflows können Sie hier mit Param ($ZuÜbergebendeVariablen) entsprechende Schnittstellen definieren. Mithilfe des Abschnitts Node Computername {Ihr ScriptCode} können Sie festlegen auf welchen Computern das Gewünschte passieren soll. Ein Konfigurationsabschnitt (Configuration) kann mehrere unterschiedliche Node Abschnitte enthalten.

3.4.4.1   Rolle hinzufügen/entfernen

Um eine aus dem Servermanager bekannt Rolle oder Feature hinzuzufügen oder zu entfernen gibt es die Möglichkeit innerhalb eines Node Abschnittes einen weiteren, namens WindowsFeature NameIhrerWahl {…} einzufügen. Dieser sorgt dafür, dass die von Ihnen angegebenen Rollen und Features auf dem entsprechenden Computer hinzugefügt bzw. entfernt werden. Leider muss für jedes Feature ein separater Abschnitt erstellt werden. So können Sie im handumdrehen einem Rechner andere Aufgaben zukommen lassen. Der Aufruf, um dies gleich auf 3 Computern Ihrer Wahl durchzuführen erfolgt in der letzten Zeile.

10.         Configuration AddFeatures {

11.          Param ([String[]]$Computer=localhost)

12.          Node $Computer {

13.           WindowsFeature XPS {

14.            Ensure = "Present" # zum deinstallieren: "Absent"

15.            Name = "XPS-Viewer"

16.           }

17.           WindowsFeature WLAN {

18.            Ensure = "Present" # zum deinstallieren: "Absent"

19.            Name = "Wireless-Networking"

20.           }

21.          }

22.         }

23.         AddFeatures Computer Rechner1,Rechner2,Rechner3

Dies erzeugt im aktuellen Verzeichnis ein Unterverzeichnis mit der Bezeichnung AddFeatures  (Aus Zeile 10) und darin sogenannte MOF (MOF = Managed Object Format) Dateien für jeden Node (im Beipiel: Rechner1.mof, Rechner2.mof und Rechner3.mof). Wenn Sie ein anderes Verzeichnis wünschen geben Sie es mittels –OutputPath an:

AddFeatures –OutputPath C:\VerzeichnisIhrerWahl

3.4.4.2   Konfiguration auf die Zielcomputer anwenden

Um die Konfiguration der Computer dann tatsächlich zu starten, können Sie die gespeicherte Konfiguration mittels des Cmdlets Start-DSCConfiguration ausführen:

Start-DscConfiguration -Wait -Verbose -Path C:\VerzeichnisIhrerWahl

-Verbose gibt detaillierte Informationen während des Vorgangs aus und –Wait sorgt dafür, dass die Konfiguration erst komplett abgeschlossen wird, bevor es weiter geht.

Möchten Sie die Konfiguration erneut auf einem Zielcomputer sicherstellen, brauchen Sie nicht noch einmal alle Schritte durchalufen, sondern können einfach mittels

Invoke-CimMethod –Namespace ` root/Microsoft/Windows/DesiredStateConfiguration –ClassName ` MSFT_DSCLocalConfigurationManager –MethodName ` PerformRequiredConfigurationChecks –ComputerName NamesDesComputers ` –Arguments @{Flags=[uint32] 1}

die Konfiguration erneut ausrollen. Anstatt nur einem Computernamen können Sie natürlich auch mehrere angeben.

3.4.4.3   Dateien und Ordner kopieren/löschen und Abhängigkeiten definieren

Sie können mit DSC nicht nur einfach Rollen hinzufügen/entfernen, sondern auch gleich noch bestimmte Ordner-Strukturen oder Dateien auf die Systeme kopieren lassen. Entfernen ist natürlich auch möglich. Der grau markierte Bereich ist identisch mit dem Skript im vorangegangenen Abschnitt.

10. Configuration FileServerMitDateien {

11.  Param ([String[]]$Computer)

12.  Node $Computer {

13.   WindowsFeature Dateiserver {

14.    Ensure = "Present" # zum deinstallieren: "Absent"

15.    Name = "File-Services"

16.   }

17.           File KopierAktion {

18.             Ensure = "Present"  # Mit "Absent" löscht man

19.             Type = "Directory" # Standard ist "File"

20.             Recurse = $true # inklusive komplettem Inhalt

21.             SourcePath = "\\Servername\Share" # Pfad wo er die Dateien bzw. Ordner her holt

22.             DestinationPath = "C:\Share" # Pfad wo die Ordner-Struktur auf dem unter Node genannten Computer abgelegt werden sollen

23.             DependsOn = "[WindowsFeature]Dateiserver" # Stellt sicher, dass zuerst die WindowsFeatures aus dem Abschnitt Dateiserver abgearbeitet sind

24.           }

25.  }

26.