PowerShell - Best Practice - bessere Skripts erstellen


PowerShell bietet relativ viel Freiraum in der Gestaltung der Skripts. Um Skripts leserlich und verstĂ€ndlich zu schreiben, ist es von Vorteil, wenn bestimmte Regeln eingehalten werden. In PowerShell können fĂŒr die ErklĂ€rung des Codes Kommentare eingefĂŒgt werden, bzw. können im Header eines cmdlets Informationen zum Skript und eine Hilfe hinterlegt und die möglichen Parameter dokumentiert werden. Das Ziel sollte sein, dass der Code möglichst selbsterklĂ€rend geschrieben wird, was so manchen Kommentar ĂŒberflĂŒssig macht und eine spĂ€tere Anpassung durch einen selbst oder durch andere vereinfacht. ZusĂ€tzlicher Code fĂŒr eine Fehlerbehandlung kann die StabilitĂ€t erhöhen und die Fehlersuche beschleunigen. Der Einsatz eines geeigneten Editors unterstĂŒtzt bei der Entwicklung, siehe PowerShell Editoren im Vergleich: ISE, Visual Studio Code.

selbsterklÀrend schreiben

  • Beim Aufruf sollte der ganze cmdlet-Namen verwendet werden (keine alias). 
    Als Beispiel kann das cmdlet: "Get-ChildItem" auch mit dem Alias "gci" aufgerufen werden. Beim Lesen des Sourcecode lÀsst der Name "Get-ChildItem" rein anhand des Namens den Einsatzzweck der Funktion vermuten, laut Hilfe: get-help get-childitem:
    "Gets the items and child items in one or more specified locations."
  • Als Parameter sollten sogenannte "Named Parameter" in Skripts verwendet werden:
    Als Beispiel könnten alle Dateien eines bestimmten Ordners mit dem cmdlet: "Get-ChildItem" angezeigt werden:
    Nicht empfohlen: "get-childitem c:\temp", oder "gci c:\temp"
    Der Aufruf sollte wie folgt verwendet werden:
    "get-childitem -Path c:\temp"
  • Dokumentation innerhalb des Codes
    • In PowerShell ISE kann mit Ctrl+J mit Cmdlet (advanced) eine Vorlage fĂŒr den PowerShell-Header eingefĂŒgt werden, siehe: Cmdlet (erweiterte Funktion)
  • Bei Befehlen die nicht selbsterklĂ€rend sind: Kommentare hinzufĂŒgen: 
    • Ein einzeiliger Kommentar kann mit  "#"  erstellt werden
      #Beschreibung des folgenden Aufrufes
    • mehrzeilige Kommentare beginnen mit einem "<#" und enden mit "#>"
      <#
          mehrzeiliger
          Kommentar
      #>

FĂŒr die Funktionsnamen gibt es vorgegebene PrĂ€fixe. FĂŒr eigene Funktionen sollte ein gĂŒltiger PrĂ€fix (Verb) verwendet werden:

Get-Verb - zulĂ€ssige cmdlet-Namen fĂŒr eigene Funktionen

Mit dem Befehl Get-Verb ist es möglich alle zulĂ€ssigen Verben fĂŒr eigene Befehle anzeigen zu lassen:

PS C:\Windows\system32> get-verb
Verb        Group
----        -----
Add         Common
Clear       Common
Close       Common
Copy        Common
Enter       Common
Exit        Common
Find        Common
Format      Common
Get         Common
Hide        Common
Join        Common
Lock        Common
Move        Common
New         Common
Open        Common
Optimize    Common
Pop         Common
Push        Common
Redo        Common
Remove      Common
Rename      Common
Reset       Common
Resize      Common
Search      Common
Select      Common
Set         Common
Show        Common
Skip        Common
Split       Common
Step        Common
Switch      Common
Undo        Common
Unlock      Common
Watch       Common
Use         Other

 

Verb        Group
----        -----
Backup      Data
Checkpoint  Data
Compare     Data
Compress    Data
Convert     Data
ConvertFrom Data
ConvertTo   Data
Dismount    Data
Edit        Data
Expand      Data
Export      Data
Group       Data
Import      Data
Initialize  Data
Limit       Data
Merge       Data
Mount       Data
Out         Data
Publish     Data
Restore     Data
Save        Data
Sync        Data
Unpublish   Data
Update      Data

 

Verb        Group
----        -----
Approve     Lifecycle
Assert      Lifecycle
Complete    Lifecycle
Confirm     Lifecycle
Deny        Lifecycle
Disable     Lifecycle
Enable      Lifecycle
Install     Lifecycle
Invoke      Lifecycle
Register    Lifecycle
Request     Lifecycle
Restart     Lifecycle
Resume      Lifecycle
Start       Lifecycle
Stop        Lifecycle
Submit      Lifecycle
Suspend     Lifecycle
Uninstall   Lifecycle
Unregister  Lifecycle
Wait        Lifecycle

 

Verb        Group
----        -----
Debug       Diagnostic
Measure     Diagnostic
Ping        Diagnostic
Repair      Diagnostic
Resolve     Diagnostic
Test        Diagnostic
Trace       Diagnostic

 

Verb        Group
----        -----
Connect     Communications
Disconnect  Communications
Read        Communications
Receive     Communications
Send        Communications
Write       Communications

 

Verb        Group
----        -----
Block       Security
Grant       Security
Protect     Security
Revoke      Security
Unblock     Security
Unprotect   Security
 

Es sollte keine Mehrzahl fĂŒr das Verb verwendet werden, die vorgeschlagenen Verben sind alle in der Einzahl ...

keine langen Einzeiler

Durch das Verwenden mehrerer Parameter sind Befehle des Öfteren etwas schwerer zu lesen:

Get-ChildItem -Path "c:\temp" -Recurse -Depth 2 -Include "*.txt" -Force  -Exclude "*temp*" -WarningAction Continue -ErrorAction Stop

Etwas ĂŒbersichtlicher wird es, wenn die Parameter in eigene Zeilen geschrieben werden:

ZeilenumbrĂŒche

mit einem "`" am Ende der Zeile kann der Befehl auf mehrere Zeilen aufgeteilt werden:

Get-ChildItem `
    -Path "c:\temp" `
    -Recurse `
    -Depth 2 `
    -Include "*.txt" `
    -Force  `
    -Exclude "*temp*" `
    -WarningAction Continue `
    -ErrorAction Stop

Alternativ können die Parameter auch ĂŒber eine Hashtable ĂŒbergeben werden:

Splatting

Durch das Auslagern der Parameter in eine Hashtable, können diese ĂŒbersichtlicher gestaltet werden:

$HashArguments = @{
  Path = "c:\temp"
  Recurse = $true
  Depth = 2
  Include = "*.txt"
  Force = $true
  Exclude = "*temp*"
  WarningAction = "Continue"
  ErrorAction = "Stop"
}
Get-ChildItem @HashArguments

ein Einsatzzweck pro Funktion

Funktionen sollten, wie auch bei anderen Skriptsprachen, einen bestimmten Einsatzzweck haben und nicht mehrere Aufgaben in einer Funktion vereinen. Als Beispiel werden mit dem Befehl "Get-ChildItem" alle Dateien oder Ordner eines Verzeichnisses ausgegeben. Um auf den Dateien eine bestimmte Aktion, zum Beispiel das Löschen aller Dateien (Items) auszufĂŒhren, kommt ein weiteres cmdlet zum Einsatz: "Remove-Item". Die beiden cmdlet können im Aufruf kombiniert werden: 

Get-ChildItem -path "c:\temp" | Remove-Item

Get-ChildItem hat als Einsatzzweck eine Liste von Dateien zu liefern, Remove-Item einzig ein bestimmtes Item zu entfernen (löschen). Das Beispiel ist fĂŒr die interaktive Verwendung in PowerShell und sollte nur die genau definierte Aufgabe der Cmdlets verdeutlichen.

Funktionen nicht mit einem exit beenden

Funktionen sollten im Fehlerfall mit einem throw beendet werden, nicht mit einem exit. Der Grund dafĂŒr ist, dass beim Verwenden von exit das komplette Skript beendet wird. WĂŒrde hingegen "Throw" verwendet werden, kann ein Fehler der Funktion mit einem try/catch behandelt werden. Ohne try/catch beendet sich das Skript an dieser Stelle dennoch mit einem Fehler.

function test{
	Param($x)
	if($x){
		Write-Output $x 
	}else{
		Throw 'no $X passed' 
	}
}

try {
   Test 
} catch {
    Write-Output "Param x fehlt"
}

Abstraktion in eigene Funktionen, wenn es Sinn macht

Funktionen machen Sinn, wenn diese einen Mehrwert bieten:

  • Wenn ein bestimmter Codeblock mehrfach verwendet werden soll
  • FĂŒr bestimmte Logiken, die sich schlichtweg nur mit Funktionen umsetzen lassen.
  • Wenn das Skript dadurch zuverlĂ€ssiger wird.
  • Wenn das Skript dadurch verstĂ€ndlicher und einfacher wird

Als Beispiel könnte die Funktion Get-ChildItem in eine neue Funktion verpackt werden und im Anschluss ĂŒber diese aufgerufen werden:

<#
.Synopsis
   Get-MyChildItem
.DESCRIPTION
   Wrapper fĂŒr Get-ChildItem
.EXAMPLE
   Get-MyChildItem
#>
function Get-MyChildItem
{
    Param
    (
        $Path
    )
    Get-ChildItem -path $path

}

#Aufruf der Funktion:
Get-MyChildItem -path "c:\temp"

Der Aufruf der eigenen Funktion ist in dem Beispiel genauso leserlich, wie die erstellte Funktion: Das Beispiel macht in der Form natĂŒrlich keinen Sinn, es steht aber reprĂ€sentativ fĂŒr eine Funktion, deren einzige Aufgabe es ist eine andere Funktion aufzurufen. Ich habe bereits Beispiele gesehen, bei denen eine eigene Funktion das Skript weder leserlicher, noch einfacher macht. Richtig kompliziert wird es, wenn eine eigene Funktion eine andere aufruft, diese wiederum eine weitere Funktion usw. Möglich, dass der Verfasser zum Zeitpunkt des Erstellen des Skriptes zwar noch einen Überblick hat, aber sich nach einiger Zeit dabei wiederfindet das Skript erst einmal reverse-engineeren zu mĂŒssen, bevor er eine Anpassung daran machen kann und noch schwerer hat es eine andere Person. An dieser Stelle macht etwas Mitleid mit den Personen die das Skript spĂ€ter lesen oder apassen sollen durchaus Sinn. Zudem sollte nicht vergessen werden, dass eine Funktion, wie bereits erwĂ€hnt, nach Möglichkeit nur eine einzige Aufgabe haben soll: Weniger ist da oft mehr und je einfacher desto besser.

positive Bewertung({{pro_count}})
Beitrag bewerten:
{{percentage}} % positiv
negative Bewertung({{con_count}})

DANKE fĂŒr deine Bewertung!

Aktualisiert: 10.09.2021 von Bernhard 🔔


Top-Artikel in diesem Bereich


Windows PowerShell Skript erstellen und ausfĂŒhren
Im einfachsten Fall ist ein PowerShell-Skript eine Textdatei mit einer Reihe von PowerShell-Befehlen. Als PowerShell Skript versteht man eine Datei mit der Endung .ps1. Die Skriptdatei kann eine Sammlung von Befehlen, Funktionen oder Cmdlets enthalten.

Windows PowerShell Befehle: commands im Überblick
die verfĂŒgbaren PowerShell Befehle können mit dem Befehl:

PowerShell Loops und Array
Ein Array speichert mehrere Werte, Àhnlich einer 2 spaltigen Tabelle.

Fragen / Kommentare