PowerShell – 11 – Funktionen (Functions)

Unter PowerShell gibt es ebenso wie in VBS Funkionen. Dennoch gibt es bei der Verwendung dieser einige Besonderheiten zu beachten. Funktionen müssen explizit aufgerufen werden damit der Code innerhalb der Funktion ausgeführt werden kann. Innerhalb eines Scriptes werden Funktionen über ihren Namen aufgerufen. Optional können natürlich auch hier Parameter übergeben werden. Im Gegensatz zu VBS müssen die Funktionen innerhalb eines Scriptes erst definiert werden bevor man sie aufgerufen kann. Wird die Funktion im Script aufgerufen, bevor sie initialisiert wurde, läuft das Script in einen Error wie in dem folgenden Beispiel.

Meinst das lautet dieser wie folgt „Time : Die Benennung „Time“ wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.In Zeile:3 Zeichen:1+ Time1„. Um das Script korrekt ausführen zu können, muss die Funktion vorher wie im nächsten Script definiert sein.

Da nun die Funktion vor dem eigentlich Aufruf initialisiert wurde, könnte diese auch aufgerufen werden. Funktionen werden immer nach einem Schema aufgebaut.

EDIT November 06.11.2013,10:48 Uhr
Thomas Franke (http://www.thomas-franke.net) hat für dieses Problem eine sehr gute Idee. Wenn das Hauptscript am Anfang stehen soll, so kann man dies erreichen  „[…] indem man auch das Hauptscript als Funktion definiert.“  Thomas liefert gleich das passende Beispiel für das erste von mir gezeigte Script mit.

Die Hauptfunktion Main ruft unsere eigentliche Funktion Time auf. Dies mag auf den ersten Blick komplizierter aussehen, ist aber durchaus von Vorteil bei größeren Scripten, da der Hauptteil unseres Scriptes nicht hinter endlosen Funktionen verschwindet sonder ganz oben stehen kann. An dieser Stelle noch Danke an Thomas für den Tipp.

Aufbau einer Funktion

Funktionen beginnen immer mit dem Keyword Function. Dieses zeigt an, dass eine Funktion folgt. Darauf folgt der Funktionsname, welcher selbst gewählt werden kann, anschließend folgen evtl. benötigte Parameter in Klammer. Schlusslicht und wohl wichtigstes Element bildet der Script-Block in geschwungenen Klammern. Das Ganze sieht dann so aus.

 Parameter einer Funktion übergeben

Es gibt mehrere Wege eine Funktion aufzurufen und dabei Parameter zu übergeben. Im folgendem Script habe ich eine Funktion namens Time implementiert. Diese hat die simple Aufgabe einem Benutzer entsprechend der Uhrzeit ein „Guten Morgen“, „Guten Tag“ oder ein „Guten Abend“ auszugeben. Die Funktion Time kann genau einen Parameter, den Namen des Anwenders, übergeben bekommen. Dies kann über mehrere Weg realisiert werden.  Dazu ruft man die Funktion mit ihrem Namen einfach auf. Der Parameter -name  kann per Tab (Tab-Taste zur Autovervollständigung drücken) eingeblendet werden und man kann den gewünschten Namen einfach anhängen.

Eine Variante ist also den Namen einfach anhängen oder in Hochkomas schreiben und natürlich anhängen, Time -name Stefan Rehwald oder Time -name „Konrad Ernst Otto Zuse“. Eine weitere Variante ist es die Variablen in Klammern zu übergeben, dann müssen Strings in jedem Fall in Hochkommas geschrieben werden. Time („Rudolf Diesel“).  Ebenso ist es möglich auch Variablen anzuhängen. Time ($User).

Das ist im Großen und Ganzen das ‚Geheimnis‘ um Funktionen.

rewe

PowerShell – 10.1 – Komplexe Parameter

Es gibt noch eine weitere, etwas komplexere Methode Parameter in einem Script zu definieren. Anstatt einfach in der Funktion Param die Variablen zu definieren, kann man mit dieser Variante auch die benötigte Reihenfolge der Verbalen festlegen und ob dieser Wert zwingend notwendig ist. Dazu wird wie gewohnt der Param-Block geschrieben. Neu ist in dieser Variante nur, dass vor der Definition der Variable wie beispielsweise [string]$EingabeString, noch einmal Parameter mit bestimmten Eigenschaften in eckige Klammern geschrieben wird. Dies sieht wie folgt aus  [Parameter (Mandatory, Position)]. Hier wird für den folgenden Parameter definiert, ob dieser zwingen angegeben werden muss und in welcher Reihenfolge er erscheinen soll.

Eigenschaften des Parameters

Mittels Mandatory (dt. Pflichtangabe), eine Switch-Eigenschaft wird festgelegt, ob der Parameter zwingend angegeben werden muss. Mit der Eigenschaft Position kann die Stelle des Parameters angeben werden, wann dieser am Script angefügt werden soll, diese Angabe ist optional. Im Beispiel wird nur ein Parameter gewünscht. Dieser muss angeben werden und an Position eins hinter dem Script stehen.

Wie im Bild deutlich wird, wird man beim Starten des Scriptes aufgefordert den Benutzernamen anzugeben, da dieser notwendig für die weitere Ausführung des Scriptes  ist. Hängt man den Parameter wie gewohnt einfach  an, so fragt das Script diesen Parameter nicht ab.

Parameter eingeben

Möchte man mehrere Parameter vordefinieren, so müssen diese mit einem Komma separiert werden. Im nächsten Script muss der Benutzername und das Alter angeben werden. Die Postleitzahl ist hingegen keine Pflichtangabe.

Dem entsprechend wird die Postleitzahl beim Starten des Scriptes ohne die Angabe von Parametern nicht mit aufgeführt.

Parameter angeben

Soll dennoch die Postleitzahl mit angegeben werden muss dies dem Script als Parameter mit übergeben werden. Anschließend erfolgt die Aufforderung „Postleitzahl bitte eingeben“.

Parameter angeben

Die Variable $PLZ ist durch die Angabe des Switch-Parameter -PLZ nun $True, somit spring das Script in die Else-Abfrage.

So viel zu den erweiterten Parametern.

rewe

 

PowerShell – 10 – Einem Script Parameter übergeben

Ich wurde gefragt wie man einem Script in PowerShell einen oder mehrere Parameter übergeben kann. Dies ist relativ einfach zu bewerkstelligen. Man muss die zu übergebenden Parameter vorher im Script definieren und den definierten Variablen einem Datentyp zuschreiben. Dazu schreibt man den Datentyp in eckige Klammern, dieses Konstrukt setzten man dann vor die Variable, beispielsweise für einen String, [string]$EingabeString = „“. So ist klar definiert das es sich bei dieser Variable $EingabeString ausschließlich um einen String handelt. Was für Datentypen gibt es innerhalb von PowerShell?

Datentypen

In der folgenden Tabelle sind am häufigsten genutzten Dateitypen aufgelistet.

Datentyp Beschreibung
[string] Zeichenkette fester Länge von Unicode Buchstaben
[char] 16 Bit Unicode-Zeichen. Nur ein Zeichen
[byte] vorzeichenloses 8 Bit Zeichen
[int] vorzeichenbehafteter 32 Bit Integer
[long] vorzeichenbehafteter 64 Bit Integer
[bool] / [switch] Boolean, True/Wahr oder False/Falsch
[decimal] 128 Bit dezimal Wert
[single] Einfache Genauigkeit, Gleitkommazahl
[double] Doppelte Genauigkeit, Gleitkommazahl
[DateTime] Datum und Zeit
[xml] XML Objekte
[array] Feld, Array
[hashtable] Hashtabelle, Steuerwerttabelle

Der Parameterblock im Script

Vordefinierte Parameter werden in der Funktion Param() definiert. Dort werden die Variablen samt des Datentyp und ihrem Wert definiert. Mehre Variablen werden mit einem Komma getrennt. Dies kann wie im folgenden Beispiel definiert aussehen.

Dieses kleine Script kann einen Integer, eine String und eine Bool als Parameter übergeben bekommen. Die Parameter können dann, beispielsweise über die Console auch abgerufen und angezeigt werden. Die Reinfolge der Parameter ist in diesem Beispiel beliebig wie das folgende Bild zeigt.

Paramter übergeben I

Ebenso können hier Parameter auch einfach weggelassen werden, wenn sie nicht von Nöten sind. Parameter können auch im Script vordefiniert werden, im Falle sie werden nicht angegeben, sind aber für das Script von Bedeutung. Dazu habe ich ein simples Script geschrieben, diese möchte die Eingabe eines Ortes, einer Temperatur in Grad Celsius oder Grad Fahrenheit und einen Bool-Wert ob es sich bei der Eingabe um Celsius oder Fahrenheit handelt. Der Boolean ist optional. Für alle drei Parameter gibt es vordefinierte Werte, so kann das Script auch komplett ohne die Angabe von Parametern gestartet werden.

Zuerst starte ich das Script ohne Angabe von Parametern, anschließend gebe ich nur Temperatur und Ort an. Man sieht hier die vordefinierten Werte, da keine Parameter angegeben wurden.

Temperatur Script I

Im zweiten Anlauf habe ich 24,9 Grad Celsius für die Stadt Leipzig angegeben. Die Kommastelle wird bei Dezimalzahlen mit einem Punkt angegeben.

Temperatur Script II

In folgender Konstellation habe ich diesmal einen negativen Wert in Fahrenheit angegeben. Dazu habe ich den Parameter -Fahrenheit angegeben, somit steht in der Textausgabe Fahrenheit oben und es wird korrekt umgerechnet.

Temperatur Script III

Ist der Schalter auf $Fahrenheit $True so spring das Script in den Else-Teil. Dort wird dann Grad Fahrenheit in Grad Celsius umgewandelt. In der letzten Zeile wird die Formatierung auf minimal zwei Stellen nach dem Komma und maximal drei Stellen festgelegt. Dies wird mittels {0:#.00#} realisiert.

Formatierung

Die erste Null ist ein Platzhalter für die Variable $Temperatur. Würde es noch einen zweiten Wert geben, der formatiert werden soll, dann würde aus dieser Null eine Eins, {1:#.00#}, da dies dann der zweite formatierte Wert in dem Ausgabestring wäre. Die beiden Nullen sagen aus, dass es genau zwei feste Stellen nach dem Komma gibt. Die Raute vor dem Punkt besagt das an dieser Stelle eine beliebig langer Zahlenwert stehen kann. Die Raute nach dem Punkt definiert hingegen eine variable Stelle und wird hier nur angezeigt, wenn nicht glatt gerundet wird.

Allgemeines über Parameter für Scripte und Cmdlets

Parameter sind Zusatzinformationen, die man dem Script übergibt. Parameter werden nach der Angabe des Scriptnamens durch ein Leerzeichen getrennt angegeben.Es gibt positionale und benannte Parameter. Diese sind besonders für Cmdlets wichtig. Positionale Parameter müssen genau in der richtigen Reihenfolge angegeben werden, damit diese korrekt von den Cmdlets verarbeitet werden können. Benannte Parameter sind an keine besondere Reihenfolge gebunden. Diese bestehen aus einem Paar, als erstes wird der Name des Parameters, wie gewohnt beginnend mit einem Bindestrich (-Parametername), genannt und anschließt der dazugehörige Wert. Dies ermöglicht eine eindeutige Zuordnung der Werte ohne besondere Reihenfolge. Die Angabe der Parameternamen ist nicht case-sensitiv, d.h. Groß- und Kleinschreibung ist nicht von Bedeutung, lediglich aus Schönheitsgründen. Der Name kann auch abgekürzt  werden, solange er dabei eindeutig bleibt. Eine Besonderheit gilt bei den Switch-Parametern, diese bestehen nur aus dem Parameternamen. Wird der Switch-Parameter mit angeführt, so aktiviert er eine bestimmte Funktion (der Wert wird $True). Wird dieser nicht mit genannt, so bleibt der Wert $False oder wie im Script / Cmdlet vordefiniert.

Soweit zur Übergabe von Parametern.

rewe

PowerShell – 09 – Processbar (Ladebalken) verwenden

Bei einigen Abfragen dauert es manchmal etwas länger. Da wäre es doch hilfreich zu wissen wie weit man im Prozess vorangeschritten ist. Genau für solche Fälle gibt es in PowerShell die Funktion Write-Progress. Mit dieser lässt sich anzeigen an welcher Stelle das Script momentan ist, bzw. die Schleife die durchlaufen wird. Die Ausgabe der Information erfolgt  schriftlich und/oder mittels eines Ladebalken (Processbar). Wer die Cmdlets von Quest bereits nutzt, wird bei einigen Abfragen wie beispielsweise Get-QADUser den Processbar bereits kennen.

Das einfachste Beispiel um ein Processbar zu nutzen ist eine For-Schleife. In dem folgendem Code-Beispiel wird lediglich bis 100 hochgezählt. Da der Vorgang keinerlei größerer Rechenleitung bedarf, geht es ziemlich flott. Doch damit man überhaupt etwas von dem Prozess mitbekommt, habe ich die Funktion Sleep verwendet.

Sleep

Mit der Funktion Sleep lässt sich ein Script für eine bestimmte Zeitspanne anhalten. Dies kann beispielsweise bei der Verarbeitung  von Daten am Active Directory nützlich sein. Hier können unter Umständen bei einigen Aktionen über das Script mehr, bzw. schneller Daten geschrieben werden, als der Server der Datenbank verarbeiten kann. Sleep hat zwei wichtige Parameter, das wäre einmal -Seconds und zum anderen -Milliseconds. Im folgendem Script wird das Script bei jedem Schritt jeweils um 100 ms angehalten.

Man sieht beim Ausführen eine leichte Verzögerung. Wem das nicht reicht, setzt einfach den Wert des Parameters -Milliseconds auf 1000 oder man änder den Paramter zu – Seconds 1. Dann wartet das Script jeweils 1 Sekunde zwischen den Schritten. Nun das Ganze mit einem Processbar.

Write-Progress

Mit der Funktion Write-Progress wird ein Processbar erzeugt. Mit dem Parameter -Activity wird das oberer Textlabel befüllt. Der Parameter -PercentComplete dient zur Darstellung des Processbar und ist optional. Wird dieser Parameter weggelassen, erscheint dennoch ein Processbar, mit dem allerdings nichts geschieht. Wie gibt man den Takt für den Processbar korrekt an? Dazu muss der Prozentanteil exakt ermittelt werden, hierbei kommt die allseits bekannte Prozentrechnung ins Spiel.
\frac{i}*100{Gesamtsumme}
Sind in einem Array beispielsweise mehre Daten wie Usernamen hinterlegt, so kann man die Anzahl der User mittels $Array.Count ermitteln. Das ist die Gesamtsumme. Wichtig wird diese Formel bei allen Schleifentypen außer For. Weiter unten im Text werde ich noch ein Beispiel mit der Foreach-Schleife erläutern. Zu guter Letzt gibt es noch den Parameter -Status. Hier kann der aktuell zu bearbeitende Wert ausgeben werden. Hier nun das simple Beispiel, ohne rechenaufwand.

Wer ebenso wie ich die PowerShell ISE nutzt, dem wird dies dann wie folgt in einer Grafik angezeigt. Der Zähler ist bei $i=17. So lassen sich über den Parameter das aktuell bearbeitete Objekt anzeigen.

ProgressBar in der ISE

Für alle die das Script in der Console ausführen, sieht das Ganze dann so aus.

ProgressBar in der Console

Jetzt möchte ich noch ein paar Beispiele mit For-Schleifen und auch mit den bereits angekündigten Foreach-Schleifen aufzeigen. Im folgendem Script lasse ich einfach alle Files des Windows-Ordners aufzählen.

Get-ChildItem

Zunächst werden alle Elemente in die Variable $files mit Hilfe der Funktion Get-ChildItem und mit dem zugehörigen Parameter -Path geladen. -Path gibt den Pfad des Ordners an.  Für den Parameter -PercentComplete darf der maximal Wert nicht höher als 100 sein. Also muss der Wert der Files, hier z.B. 121, als 100 angenommen werden. Wir rechnen hier ($i * 100 )/121 um den prozentualen Wert der Variable $i zu ermitteln. Aber nun von der grauen Theorie in die bunte Praxis.Exciting

$files | Select name listet lediglich nach dem Ausführen der For-Schleife alle Werte des Array $files auf. Wieder habe ich die Sleep Funktion benutzt, da dieser Job einfach zu schnell für unsere Testzwecke laufen würde.

ProgressBar ermittelte Daten

In einigen meiner Scripte nutze ich den Parameter -PercentComplete nicht. Dennoch werde ich hier nun die versprochenen Foreach-Scripte zeigen und näher bringen. Hier muss man sich ein paar nur Gedanken zur Variable $i machen, ansonsten läuft der Vorgang analog zur For-Scheife. Die Variable $i wird für die Angabe des Parameters PercentComplete gebraucht und wird in der Foreach-Schleife hoch gezählt. Um eventuelle Fehler zu vermeiden wird $i vor und nach der Schleife Null gesetzt.

Die Angabe der zu durchsuchenden OU kann in kanonischer Form, sprich Stefan.Rehwald/Arbeitbereich/Test/TEST-Gruppe oder als DN (distinguished name), also OU=TEST-Gruppe,OU=TEST,OU=Arbeitsbereich,DC=Stefan,DC=Rehwald angegeben werden.

ProgressBar ermittelte User

Auch hier habe ich wieder Sleep verwenden, da dieser Vorgang bei den wenigen User-Objekten rasch durchgeführt ist. Meist nutze ich die Prozentanzeige nicht, sattdessen nehme ich den Counter der zu bearbeitenden Daten und lasse die Variable $i wie im ersten Foreach-Script hochzählen. Das Ganze lasse ich mir dann über Parameter -Status ausgeben.

 Im zweiten Script habe diesmal den DN ansatt des kanonischen Wertes angeben. Wie man auf der nachfolgenden Grafik sehen kann, wird der Ladebalken, auch wenn er nicht genutzt wird, angezeigt. Es lassen sich natürlich auch beide Scriptvarianten kombinieren.

ProgressBar ermittelte User II

 So weit zum Thema Processbar, Ladebalken.

rewe