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!

Ver├Âffentlichung: 09.09.2021 von Bernhard | Translation English |­čöö

Ô×Ę PowerShell Editoren im Vergleich: ISE, Visual Studio Code | Ô׎ Windows PowerShell | PowerShell Log-Files: Logging in eine Textdatei - write to file Ô×Ę

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.


PowerShell Log-Files: Logging in eine Textdatei - write to file

Logfiles in PowerShell k├Ânnen ├╝ber den Befehl Out-File, ├╝ber eine eigene Funktion oder ├╝ber das in PowerShell integrierte Transcript erstellt werden.


PowerShell regex - Einf├╝hrung und Beispiele

Regex kann in den meisten Skriptsprachen nahezu gleich verwendet werden, daher habe ich die Grundbegriffe und Funktionsweise in einem gesonderten Beitrag zusammengefasst, siehe┬áRegex - ├ťberblick. Dieser Beitrag beinhaltet Speziali├Ąten bei der Verwendung von Regex in PowerShell, sowie die Beispiele des Regex - Grundlagen Artikels. ┬á Powershell: verschiedene Regex-Varianten PowerShell verf├╝gt ├╝ber eigene Regex-Operatoren, als Beispiel: -match oder -replace. Nachdem Po...

Fragen / Kommentare