Archiv der Kategorie: Tipps

Tipp des Tages: Die öffentliche IP-Adresse als Objekt erhalten

Auf https://techibee.com/powershell/powershell-get-public-ip-address-of-my-computer/3027 hat der Autor einen genauso praktischen wie anschaulichen Tipp veröffentlicht, den ich an dieser Stelle 1:1 wiedergebe.

Die Abfrage der öffentlichen IP-Adresse geht sehr einfach mit Hilfe eines kleinen Webservice unter der Adresse http://ipinfo.io.

Wie ruft man einen (REST-) Webservice per PowerShell auf? Natürlich per Invoke-RestMethod-Cmdlet:

Invoke-RestMethod -Uri http://ipinfo.io

Nett ist, dass der Aufruf in Bruchteilen einer Sekunde erstaunlich akkurat die Adresse des nächsten Telekom-Knotens liefert (entweder gibt es inzwischen einen in Esslingen und Umgebung oder diese Information wurde aus anderen Angaben abgeleitet). Da die Rückgabe ein Objekt ist, stehen Angaben wie Stadt und Land und natürlich die Ip als Eigenschaften zur Verfügung.

Kleine Tipps für Zwischendurch – Zeitspannen formatiert ausgeben

Genau wie für DateTime-Objekt, die bekanntlich eine Datum und eine Uhrzeit repräsentieren, gibt es auch für TimeSpan-Objekte, die eine Zeitspanne repräsentieren, eine formatierte Ausgabe. Allerdings nicht ganz so intuitiv wie es sein könnte.

Gleich vorweg: Alles ist bei docs.microsoft.com natürlich vorbildlich dokumentiert:

https://docs.microsoft.com/de-de/dotnet/standard/base-types/custom-timespan-format-strings

Aber zum einen bezieht sich die Dokumentation auf die Programmiersprache C# und zum anderen, wer liest wirklich eine solche Beschreibung?

Ein TimeSpan-Objekt entsteht z.B. immer dann, wenn zwei DateTime-Objekte voneinander subtrahiert werden. Es gibt Eigenschaften wie days, hours, minutes, totaldays, totalhours usw. Während z.B. hours für den Stundenanteil steht, steht totalhours für die Zeitspanne in Stunden. Beträgt die Zeitspanne z.B. 90 min, wären hours=1 und totalhours=1.5.

Möchte man ein TimeSpan-Objekt als Teil einer Zeichenkette ausgeben, muss man die Formatbezeichner kennen. Sie lauten h, m, s und fff (für Millisekunden). Das ist irgendwie naheliegend, etwas verzwickter wird es durch den Umstand, dass auf den ersten Formatbezeichner ein Apostroph folgen und die folgenden Formatbezeichner in Apostrophe gesetzt werden müssen (wer sich das ausgedacht hat).

Hier ein paar Beispiele in loser Reihenfolge.

PS; Dies ist mein letzter Blogeintrag auf absehbare Zeit. Bei Fragen rund um die PowerShell bitte einfach eine Mail, z.B. an pm ät activetraining de.

Praxistipp: SQL-Kommandos aus einer SQL Server-Trace-Datei herausziehen

Der folgende Tipp ist sehr speziell, aber enorm praktisch und ganz allgemein ein weiteres Beispiel für die Flexibilität beim Auswerten von Xml-Dateien per PowerShell.

Ausgangspunkt ist der Microsoft SQL Server und da wiederum der SQL Server Profiler, der alle SQL-Kommandos anzeigt, die von einer Anwendung an die Datenbank geschickt werden. Das Trace-Protokoll kann im XML-Format gespeichert werden.

Der folgende Befehl zieht nur die SQL-Kommandos aus der XML-Datei heraus, so dass sie in einer separaten Textdatei gespeichert werden könnten:

Auch wenn auch der SQL Server-Profiler eine ähnliche Option bietet, den reinen SQL-Text erhält man damit nicht.

Kleiner Fix für PScribo

PScribo von Ian Brighton ist ein Modul, das ich nach wie vor gerne verwende und weiterempfehle. Auch wenn der Versionsstand immer noch < 1.0 ist, scheint das Projekt noch aktiv zu sein (https://github.com/iainbrighton/PScribo/pulse).

Unter einem deutschsprachigen Windows erschienen zuletzt „jede Menge“ Fehlermeldungen. Eine Variable $Location war nicht definiert. Der Grund war, dass im Modulverzeichnis das Unterverzeichnis „de-De“ nicht vorhanden ist. Nachdem ich das Verzeichnis angelegt und die Psd1-Datei aus dem en-Us-Verzeichnis dort hinein kopiert hatte, lief es wieder durch.

Tipp der Woche: Strings mit einem Zeichen auffüllen

Nach diesem kleinen Trick (ich behaupte einmal, dass er relativ undokumentiert ist) habe ich einige Jahre „gesucht“. Ich hatte in einmal in einem PowerShell-Skript oder einem Beispiel, das damals noch für die Version 1.0 gemacht war entdeckt, die Syntax nicht verstanden und dann aus den Augen verloren. Ausgerechnet bei einer Code Golf-Challenge auf Stackoverflow habe ich ihn per Zufall wieder entdeckt.

Die folgende Schreibweise erzeugt einen String, der aus z.B. 20 * besteht:

Eigentlich ganz logisch. Etwas kniffliger wird es, wenn z.B. eine 10×10-Matriz mit einem einzelnen Zeichen erzeugt werden soll. Das geht wie folgt:

Oder wenn man einmal wirklich zu viel Zeit hat:

String-Spielereien mit der PowerShell-Syntax, die kein Mensch wirklich braucht

Kleine Tipps für Zwischendurch: Datumszeitangaben im UTC-Format konvertieren

Datumskonvertierungen sind ein Thema für sich, insbesondere, wenn Spezialitäten wie UTC-Zeitangaben (Universal Time Coordinate) wie „20181010T110000“ ins Spiel kommen. Solche Datumswerte kommen z.B. in Ics-Dateien vor, die als Einladung zu einem Meeting verschickt werden.

PowerShell ist dank dem .NET Framework-Unterbau sehr vielseitig was den Umgang mit Datumsformat angeht, man muss eventuell nur ein wenig suchen bzw. experimentierfreudig sein.

So hat es eine Weile gedauert bis ich herausgefunden hatte wie die obige Zeichenfolge in einen DateTime-Wert konvertiert wird:

Das Geheimnis besteht darin, dass die zweite Zeichenfolge den Aufbau der ersten Zeichenfolge so abbilden muss, dass ein gültiger Datumszeitwert resultiert.

Das „T“ stört natürlich ein wenig. Der vielseitige replace-Operator lässt es unaufällig verschwinden:

Kleine Tipps für Zwischendurch: PowerShell Core-Module aus der PowerShell Gallery auflisten

PowerShell Core ist die Zukunft. Möglichst viele der Module, die Teil von Windows und Windows Server sind, sollen früher oder später auch für PowerShell Core verfügbar sein.

Modul-Autoren sollen im Modulmanifest den Eintrag CompatiblePSEditions verwenden und dort die Editionen „PSDesktop“ und „PSCore“ eintragen. Dieser Eintrag wird aber erst ab WMF 5.1 verstanden.

Aktuell ist es nicht ganz so einfach wie es sein könnte herauszufinden, ob ein Modul der PowerShell Gallery auch für PowerShell Core in Frage kommt. Diese Information verbirgt sich in der unscheinbaren Tags-Eigenschaft eines Modul.

In der PowerShell Dokumentation wird das Prinzip der Abfrage bereits ausführlich beschrieben:

https://docs.microsoft.com/en-us/powershell/gallery/how-to/finding-items/searching-by-psedition

Zwei kleine Beispiele sollen die Abfrage per Find-Module in der Praxis zeigen.

Etwas komfortabler ist der folgende Befehl, der zu allen Modulen der PowerShell Gallery die Editionen angibt, unter denen sie verwendet werden können.

Der folgende Befehl gruppiert die ganze Ausgabe nach der Eigenschaft Edition:

Wenn die Abfrage stimmt, sind von den über 3.200 Modulen der PowerShell Gallery nur etwas über 200 Module mit einem PSEdition-Information versehen. Und da man in die Tags-Eigenschaft alles eintragen kann, gibt es auch unterschiedliche Schreibweisen.

Wer bei den installierten Modulen die Edition abfragen möchte, verwendet dazu die CompatiblePSEditions-Eigenschaft:

oder

Wenn in der PowerShell-Konsole oder ISE nichts ausgegeben wird, liegt es an der PSModulePath-Umgebungsvariablen. Nur bei PowerShell Core ist das Verzeichnis c:\Program files\powershell\6\Modules mit dabei.

Tipps für Zwischendurch: Fenster auflisten mit der EnumWindows-API-Funktion

Vor kurzem fand ich nach der Suche nach einer Lösung auf Stackoverflow einen Link auf ein PowerShell-Skript, das ich vor vielen Jahren erstellt hatte. Leider hatte der Autor meinen Namen nicht erwähnt, sonst wäre ich bei Stackoverflow zu unverhofftem Ruhm gekommen. Außerdem verwies der Autor des Beitrags auf meinen alten Blog powershell-knowhow.de, den es schon länger nicht mehr gibt. Da ich das Skript von damals auf diversen Festplatten nicht wiederfinden konnte, musste ich mich noch einmal an die Arbeit machen. Wie sooft war das Ergebnis besser als das Original, da es deutlich kürzer geworden ist. Das ist generell ein Erfahrungswert, den ich aus über 10 Jahren PowerShell ziehen kann, und der vermutlich universell gilt. Um auf die kürzeste Lösung zu kommen, muss man sich in den Bereich, in dem die Lösung gesucht wird, bereits ziemlich gut auskennen.

Wie dem auch sei, die folgende Function Get-WindowData gibt Fensterhandle und Text zu jedem Top-Window-Fenster aus (und das sind bereits erstaunlich viele). Theoretisch lassen sich durch eine Art rekursiven Aufruf natürlich auch die Kindfenster eines Fensters auflisten, aber diese Mühe habe ich mir dann doch nicht gemacht.

Interesant und lehrreich an dem Beispiel ist die Leichtigkeit, mit der sich ein „Callback“, also eine Adresse auf eine Funktion, die von der API-Funktion pro Fenster aufgerufen wird, von PowerShell an eine Win32-API-Funktion übergeben lässt. Merkregel: Überall, wo ein Delegate erwartet wird, darf auch ein Scriptblock übergeben werden.

Kleine Tipps für Zwischendurch: Kopfzeile und restliche Zeilen einer Textdatei mit einem Befehl getrennt einlesen

Bei einer Textdatei sollen Kopfzeile und die restlichen Zeilen getrennt eingelesen werden. Aber mit einem einzigen Befehl.

Mit folgender Konstruktion ist mir das gelungen.

Zunächst ein paar Testdaten als Here-String:

Dann der Befehl, der die Textzeilen in eine erste Zeile und die restlichen Zeilen aufsplittet und das Ergebnis zwei Variablen zuweist:

Warum verwende ich kein ForEach-Object? In erster Linie weil ich es etwas „übertrieben“ finde, wenn ein Wiederholungsbefehl für einen einzigen Verarbeitungschritt verwendet werden muss. Auch wollte ich keine Variable verwenden. Dann wäre auch Folgendes möglich:

Die Variable Zeilen wird gebraucht, da ich nur so an die Länge des Array beim Zugriff auf dasselbige herankomme.

Es soll direkt nach dem Schema Textzeilen | Verarbeitung => Ergebnis funktionieren. Ohne Zwischenschritte und Variablen.

Was nicht jeder wissen dürfte, auch eim harmloser Scriptblock kann in einen Begin-, Process- und End-Block unterteilt werden. Möchte man in einen Scriptblock etwas pipen, geht das nur mit eimem vorangestellten &, das den Scriptblock ausführt.

Tja, was soll man davon halten? Es funktioniert, ok. Ist das Ganze intuitiv nachvollziehbar? Eher weniger. Auf der anderen Seite, im Vergleich zu einem Befehl in Bash oder Perl, der zum selben Resultat führt, ist die PowerShell-Variante regelrecht leicht verdaulich, denn es kommen weder reguläre Ausdrücke noch kryptische Variablen wie %%S vor;)

Tipps für Zwischendurch: COM-Methoden mit Enum-Werten aufrufen

Dieser Tipp ist etwas spezieller.

Ausgangspunkt ist eine COM-Dll (früher OLE Server oder COM Server genannt), die eine Methode Calculate anbietet, die drei Parameter besitzt. Der erste Parameter ist vom Typ eines Enum mit dem Namen „OpType“, die anderen beiden Parameter sind vom Typ int.

Das Anlegen eines COM-Objekts mit der ProgId ist einfach:

Der Aufruf von Calculate funktioniert zunächst nur, wenn für den Enum-Wert eine Zahl übergeben, z.B. eine 0.

Der Enum OpType ist zunächst nicht bekannt. Ein „Trick“ besteht darin, ihn per Enum-Befehl nachzubauen, den es ab Version 5.0 gibt.

Damit klappt auch der folgende Aufruf: