PowerShell Parameter
Sowohl beim Aufruf von PowerShell-Skripten als auch Funktionen ist die Übergabe von Parametern möglich und sollte auch genutzt werden. Die Arbeit mit "globalen" Variablen ist nicht wirklich ratsam. PowerShell unterstützt und dabei sehr gut.
PowerShell und Funktionen und Parameter
Wer eine Funktion baut, macht die mit dem Ziel bestimmte Codeteile häufiger zu verwenden und den Code zu strukturieren. Man kann damit aber auch interne Verarbeitungen mit privaten Variablen aus dem Hauptprogramm auslagern. Die zur Verarbeitung erforderlichen Werte sind dann als Parameter an die Funktion zu übergeben. Hierzu gibt es zwei Wege, Parameter an eine PowerShell Funktion zu übergeben:
- Definition im Funktionskopf
Die erforderlichen Variablen können direkt hinter dem Funktionsnamen spezifiziert werden.
function Funktionsname ([int]$param1=10,[string]param2=msxfaq.de) { # Befehle }
- Definition als PARAMS-Block
Alternativ können Parameter auch als eigener Block angegeben werden. Oft ist die übersichtlicher. Dies geht auch, wenn Sie direkt ein PowerShell Skript ganz ohne Funktionen einsetzen, d.h. Parameter an das aufgerufene Skript selbst übergeben wollen
function Funktionsname { PARAM ( [int]$param1=10, [string]$param2=msxfaq.de ) # Befehle }
Wenn Sie aber die Parameter schon per PowerShell definieren, dann kann ab PowerShell 2.0 auch die "Autocomplete-"Funktion helfen, d.h. nach der Eingabe des Skripts starten Sie die Parameter mit einem "-" und nutzen dann die TAB-Taste, um durch die einzelnen Parameter zu laufen.
Typisierung
Perfekt wird es dann, wenn Sie die Parameter gleich auf "Gültigkeit" prüfen lassen. Schon eine Typisierung der Parameters verhindert viele Fehlzuweisungen
[int]$variable # 32-bit integer mit Vorzeichen [long]$variable # 64-bit integer mit Vorzeichen [string]$variable # string mit unicode characters [char]$variable # A unicode 16-bit character [byte]$variable # 8-bit Zahl [bool]$variable # Boolean True/False [decimal]$variable # 128-bit Dezimalzahl [single]$variable # Single-precision 32-bit Fließkommazahl [double]$variable # Double-precision 64-bit Fließkommazahl [xml]$variable # Xml object [array]$variable # Array [hashtable]$variable # Hashtabelle # Denkbar sind aber auch Arrays [String[]]$Userliste
Vorbelegung
Sie können einen Parameter schon mit Daten "vorbelegen". Die Angabe auf der Kommandozeile oder beim Funktionsaufruf überschreibt dann diese Vorbelegung
[string]$variablenname = "Beispielwert"
Zwang und Position und Hilfe
Weiterhin kann vorgegeben werden, ob ein Parameter zwingend angegeben werden muss und welche Position er hat, wenn Daten ohne Parameterangabe übergeben werden. Dazu setzen Sie einfach ein "[Parameter()]" davor.
- Parameter Attribute Declaration
https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/parameter-attribute-declaration?view=powershell-7.2
Den "Parameterset-Namen" kann man im Code einfach über $PsCmdlet.ParameterSetName z.B. in einer Switch-Anweisung abfragen
[Parameter( Position=0,' Mandatory=$true, HelpMessage="Dies ist eine Hilfe", ValueFromPipeline=$false, ParameterSetName="Setname"] )][string]$variablenname
Erweiterte Validierung
Über die Angabe des Typs hinaus ist sogar noch eine Validierung direkt in der Parametrisierung möglich. So kann der Aufruf eines Scripts schon bei falschen Angaben gestoppt werden und muss nicht mehr im Code selbst später geprüft werden.
# Check gegen regular expressions [Validatepattern("regexdausdruck")][string]$variablenname # erweiterte Datumsabfrage [ValidateRange( "06/10/2010 02:00:00 PM", "06/17/2010 03:00:00 PM")][DateTime]$Datum # das ganze mit errechneten Min und Max Werten [ValidateRange( [DateTime]::Now.AddDays(-7), [DateTime]::Now)] # Check gegen eine Liste von Optionen [ValidateSet("Ja", "Nein", "Vielleicht", IgnoreCase = $true)] # Verifizierung per Code, z.B. dass die Zeit nach dem 1.1. 2010 ist [ValidateScript({$_ -le [DateTime]::Now -and $_ -ge "1/1/2010"}) # Begrenzung der Element in einem Array [ValidateCount(1,3)][String[]]$UserName # erwartet einen Text mit 5-8 Buchstaben [ValidateLength(5,8)][string]$variablenname # weitere Funktionen [ValidateNotNullOrEmpty], [ValidateNotNull], [AllowNull], [AllowEmptyString], [AllowEmptyCollection]
Wem diese einfachen Checks nicht reichen, kann natürlich als Teil der Parameter aber auch später im Code natürlich eine bessere Überprüfung und Erzwingung machen. So muss man überlegen, ob ein Parameter in der Definition schon als "Mandantory" vorgegeben werden sollte oder der Code prüft, ob der Inhalt sinnhaft ist und dann den Wert zu erfragen.
param ( $dateiname = "keine Angabe" ) while (!(test-path -path $dateiname -pathtype leaf)) { write-host "Datei konnte nicht gefunden werden" $dateiname = read-host "Bitte Dateinamen eingeben" } write-host "Dateiname:" $dateiname
Mit so einer Konstruktion muss ein Anwender einen gültigen Dateinamen eingeben, ehe das Skript weiter macht. für eine Automatisierung ist dies natürlich nicht hilfreich.
Dynamische Parameter mit "dynamicparam"
Wem das dann immer noch nicht reicht, kann Parameter dynamisch ein und ausschalten, indem der Wert eines anderen Parameter ausgewertet wird.. Interessanterweise steht viel davon in der PowerShell Online Hilfe:
get-help about_Functions_Advanced_Parameters
Beim Aufruf der Funktion sollte man sich aber an PowerShell und "Komma" erinnern. Parameter werden nicht durch Komma getrennt übergeben sondern mit Leereichen. Ein Komma macht aus den zwei oder mehr Parametern ein Array, welches die Funktion als ersten Parameter erhält.
# FALSCH funktionsname wert1, wert2, wert3 #Richtig funktionsname wert1 wert2 wert3
Die Rückübergabe von Ergebnissen erfolgt ebenfalls einfach über die Pipeline, d.h. alles was das Skript einfach "ausgibt" bekommt der aufrufende Prozess zurück. Insofern muss an Ende keine Zuweisung des Ergebnisses an den Funktionsnamen stehen, sondern einfach die Variable mit dem Inhalt.
- about_Functions_Advanced_Parameters
http://technet.microsoft.com/en-us/library/dd347600.aspx - Parameter Attribute Declaration
https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/parameter-attribute-declaration?view=powershell-7.2 - Validate PowerShell Parameters Before Running the Script
http://blogs.technet.com/b/heyscriptingguy/archive/2011/01/11/validate-PowerShell-parameters-before-running-the-script.aspx - Simplify Your PowerShell Script with Parameter
Validation
http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/15/simplify-your-PowerShell-script-with-parameter-validation.aspx - Validating Parameter Input
http://msdn.microsoft.com/en-us/library/ms714432(v=vs.85).aspx - Cmdlet Dynamic Parameters
https://msdn.microsoft.com/de-de/library/dd878299(v=vs.85).aspx - Parameter Validation
http://blog.usePowerShell.com/2011/05/parameter-validation/ - New-DynamicParameter — Helper function to simplify
creating dynamic parameters
https://gallery.technet.microsoft.com/scriptcenter/New-DynamicParameter-63389a46 - Use PowerShell to Make Mandatory Parameters
http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/22/use-PowerShell-to-make-mandatory-parameters.aspx - Dynamic Parameters in PowerShell
http://www.powershellmagazine.com/2014/05/29/dynamic-parameters-in-powershell/ - How To Implement Dynamic Parameters in Your
PowerShell Functions
https://mcpmag.com/articles/2016/10/06/implement-dynamic-parameters.aspx - Using and abusing dynamic parameters
https://becomelotr.wordpress.com/2012/05/10/using-and-abusing-dynamic-parameters/
PowerShell und Kommandozeile
Analog zur Parameterübergabe für eine Funktion können Sie natürlich auch einem PS1-Skript direkt die Parameter übergeben. Da es hier aber keine Funktionsüberschrift gibt, wird der PARAM -Block direkt am Anfang geschrieben.
param( [int]$Interval=60 , [string]$domain="msxfaq.de" . [ScriptBlock]$skriptteil={(get-mailboxserver| select name)} )
Der Aufruf kann dann wie gewohnt mit Parametrisierung erfolgen.
.\scriptname -Interval 30 -Domain andere.domain
Es müssen auch nicht alle Parameter übergeben werden. Überzählige Parameter werden einfach ignoriert, d.h. vorsicht bei Tippfehlern. Erst die PowerShell 2 wertet die PowerShell diese Parameterblöcke im Skript auch derart aus, dass Sie über die AutoComplete-Funktion der Kommandozeile erreichbar werden.
Man kann hier auch die Defaults angeben, wenn der aufrufende Prozess keine Daten hinterlegt. Interessant ist hier auch die Funktion, direkt einen [Scriptblock] mit zu übergeben, welcher später einfach mit "$skriptteil" aufgerufen werden kann.
write-host "Argumente sind $args"
[CmdletBinding()]
Fast alle PowerShell-Commandlets haben Parameter wie "-verbose", "-errorpreference" etc. Diese Parameter müssen Sie nun aber nicht selbst alles codieren. Die PowerShell kennt einen "Default Parameter Set", den man nur aktivieren muss. Addieren Sie vor dem "param" einfach ein "[CmdletBinding()]" wie hier im Beispiel
[CmdletBinding()] param ( $URL = "http://www.msxfaq.de" )
Und schon haben Sie ihren Parametersatz auch um diese eingebauten Parameter erweitert. Der große Vorteil dabei ist, dass Sie nun auch "Write-Verbose" etc. einfach sinnvoll nutzen können.
Achtung: Wenn Sie in der Param-Sektion z.B. auf "$MyInvocation" zugreifen, um
z.B. den Namen des Skripts gleich in eine Variable zu schreiben, dann kommt der
folgende Fehler:
You cannot call a method on a null-valued expression.
+ CategoryInfo : InvalidOperation: (:) [],
ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvokeMethodOnNull
- [PowerShell] Built-in
support für -Debug and -Verbose für your scripts
http://mnaoumov.wordpress.com/2012/09/26/PowerShell-built-in-support-for-debug-and-verbose-for-your-scripts/ - What does PowerShell's [CmdletBinding()]
Do?
http://windowsitpro.com/blog/what-does-PowerShells-cmdletbinding-do -
about_Functions_CmdletBindingAttribute
https://learn.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_functions_cmdletbindingattribute?view=powershell-7.4
http://technet.microsoft.com/en-us/library/hh847872.aspx - Weekend Scripter:
Cmdletbinding Attribute
Simplifies PowerShell Functions
http://blogs.technet.com/b/heyscriptingguy/archive/2012/07/07/weekend-scripter-cmdletbinding-attribute-simplifies-PowerShell-functions.aspx - Erweiterte Funktionen in
PowerShell
https://www.scriptrunner.com/de/blog/powershell-erweiterte-funktionen/ - What is cmdletbinding()
http://posh2scripting.wordpress.com/2013/06/05/what-is-cmdletbinding/ - about_Automatic_Variables - $MyInvocation
https://learn.microsoft.com/de-de/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.4#myinvocation - PowerShell: The automatic variable $MyInvocation
https://renenyffenegger.ch/notes/Windows/PowerShell/language/variable/automatic/myInvocation/index
Parameter mit dem Typ "Switch"
Bei einer Funktion könne Sie auch "[switch]" als Typ angeben. In diesem Fall heisst ein vorhandensein eines Parameters, dass dieser "true" ist. Ein explizites "$true" muss nicht mehr mit angegeben werden.
function switchdemo([switch]$test ) { if( $test ) { 'true' } else { 'false' } } PS C:\> switchdemo -test:$true true PS C:\> switchdemo -test:$false false PS C:\> switchdemo -test true PS C:\> switchdemo false PS C:\> switchdemo -test $false true
Bei der Angabe von "$True" oder "$False" ist aber nur die Schreibweise mit einem Doppelpunkt (":") zulässig. Bei einem Aufruf mit Leerzeichen als Trenner wird das $False z.B. als zweiter Parameter betrachtet.
Parameter als CONFIG-Datei
In fast all meinen Skripten kommen gleich mehrere Parameter zum Einsatz. Die Skripte kommen auch bei mehreren Kunden (und mehreren Test-Umgebungen) zum Einsatz. Das gleiche Skript muss also mit unterschiedlichen Parametern aufgerufen werden.
Wenn ich mir es nun etwas einfacher machen möchte, schreibe ich mir für jede Umgebung ein PS1-Skript, welche dann die eigentliche PowerShell-Datei mit den entsprechenden Parametern aufruft. Das geht aber so richtig schön finde ich das nicht. Da gefällt es mir schon besser, die Konfiguration eines Skripts einfach komplett eine Konfigurationsdatei auszulagern. Warum nicht einfach eine XML-Datei anlegen, so wie es viele .NET-Programme auch machen. Zu jeder EXE gibt es in der Regel auch eine *.EXE.CONFIG-Datei zur Steuerung der EXE-Datei. In komplexeren Umgebungen lagere ich die Konfiguration eines Skripts daher in eine XML-Datei aus, z.B. in der Form:
<setting> <dc>dc1.msxfaq.de</dc> <ou>ou=firma,dc=msxfaq,dc=de</ou> </setting>
Im entsprechenden Skript nutze ich dann nur noch die Variablen als Parameter, die von Aufruf zu Aufruf individuell sein und ein Parameter gibt die CONFIG-Datei an.
param ( $configxml = ".\scriptname.ps1.config" , $aktion = "" ) write-host "Loading ConfigXML $($configxml)" [xml]$config = gc $configxml write host "Konfiguration dc: $($config.setting.dc)" write host "Konfiguration dc: $($config.setting.ou)"
Unterm Strich ist das sogar noch "lesbarer", wenn ich sehr viele Einstellungen in einer Konfiguration habe.
Nebeneffekt. Man kann eine individuelle Konfiguration getrennt ablegen und z.B. in GitHub ausschließen.
Parameter mit CMD
Nicht immer starten Sie eine PowerShell-Datei direkt aus einer PowerShell. Seit PS5 gibt es neben der "alten" PowerShell 2.0 auch die neue PowerShell 5, 6, 7 und höher. Manchmal gibt es Skripte die mit einer bestimmten PowerShell gestartet werden müssen, weil Funktionen noch nicht oder erst dann vorhanden sind. Über den "#requires"-Kommentar können Sie eine Version vorschreiben, aber wenn Sie das Skript dann falsch aufrufen, bekommen sie nur einen Fehler. Einige Programme starten aus Gewohnheit noch immer die alte "Powershell.exe", wenn Sie eine PS1-Datei starten wollen (z.B. PRTG - Custom Sensor) Dann hilft der "Umweg" über eine CMD-Datei, in der Sie dann die richtige PowerShell starten. Auch hier braucht man Parameter:
REM Startet das PS1-Skript mit der richtigen Powershell und Parametern "C:\Program Files\PowerShell\7\pwsh.exe" ^ -nologo ^ -NonInteractive ^ -command "& ""C:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML\get-fritzmactable.ps1"" -FBKennwort:""geheimeskennwort"" -prtg "
Diese wenigen Zeilen können Sie als CMD-Datei ablegen und entsprechend aufrufen lassen. Sie entstammt dem Beispiel Get-FritzMACTable, welches mit der PowerShell 7 arbeitet und so einfach unter PRTG aufgerufen werden kann.
- Get-FritzMACTable
- PRTG - Custom Sensor
-
Weitere Links
-
PowerShell4Admin
PowerShell - Eine Einführung für Administratoren - PSModule
- PSRemote
- WinRM
- PowerShell Klassen
- Jeffrey Snover: Der Mann der PowerShell quasi "erfunden" hat
http://www.microsoft.com/presspass/exec/de/snover/default.mspx - PowerShell Blog
http://blogs.msdn.com/b/PowerShell/ - TechNet Magazin: Remote PowerShell
http://technet.microsoft.com/de-de/magazine/2009.08.windowsPowerShell.aspx - Layman’s guide to PowerShell 2.0 remoting
http://www.ravichaganti.com/blog/?p=1780 - Mastering PowerShell eBook'
http://PowerShell.com/Mastering-PowerShell.pdf - Administrator's Guide to PowerShell Remoting (Kostenfreies ebook)
http://PowerShell.com/cs/blogs/news/archive/2010/03/03/administrator-s-guide-to-PowerShell-remoting.aspx -
PowerShell und Visual Studio
Templates für eigene PowerShell Snapins in Visual Studio installieren - http://gsexdev.blogspot.com/2005/11/using-monad-and-wmi-with-exchange-2003.html
- http://dmitrysotnikov.wordpress.com/2007/09/06/wmi-PowerShell-for-exchange-2003/
- PowerShell Tutorial 12 – Part 1: Active Directory Management
http://www.PowerShellpro.com/PowerShell-tutorial-introduction/PowerShell-tutorial-active-directory/ - Mastering PowerShell in your Lunch Break (LINKS NICHT MEHR GÜLTIG, Suche
nach einem Spiegel)
Day 1: Getting Organized http://PowerShelllive.com/blogs/lunch/archive/2007/03/26/day-1-getting-organized.aspx
Day 2: Writing Scripts and Translating VBScript
http://PowerShelllive.com/blogs/lunch/archive/2007/03/28/day-2-writing-scripts-and-translating-vbscript.aspx
Day 3: Discovering objects (COM, WMI & ADSI)
http://PowerShelllive.com/blogs/lunch/archive/2007/03/29/day-3-objects-wmi-and-adsi.aspx
Day 4: Ins and Outs of the Windows Registry
http://PowerShelllive.com/blogs/lunch/archive/2007/03/31/day-5-ins-and-outs-of-the-windows-registry.aspx
Day 5: using WMI
http://PowerShelllive.com/blogs/lunch/archive/2007/04/02/day-5-using-wmi.aspx
Day 6: ADSI Connecting to Domains/Computers and Binding to Objects
http://PowerShelllive.com/blogs/lunch/archive/2007/04/04/day-6-adsi-connecting-to-domains-computers-and-binding-to-objects.aspx
Day 7: Manage Users
http://PowerShelllive.com/blogs/lunch/archive/2007/04/05/day-7-manage-Users.aspx - PowerShell Challenge (Phyton Challenge)
http://thePowerShellguy.com/blogs/posh/archive/tags/PoSH+Challenge/default.aspx - Windows Forms Table
http://gsexdev.blogspot.com/2007/12/mailbox-folder-size-comparison.htm - Amazing But True: Things You Never Dreamt You Could Do With
Windows PowerShell.
https://www.livemeeting.com/cc/mseventsbmo/view?id=1032313506&role=attend&pw=48928A1E - Windows PowerShell Looking Good
http://technet.microsoft.com/en-us/magazine/cc510337.aspx - Windows PowerShell Week Question and Answer Log
http://www.microsoft.com/technet/scriptcenter/webcasts/psweek/day3qanda.mspx - Managing Exchange 2007 Recipients with C#
http://knicksmith.blogspot.com/2007/03/managing-exchange-2007-recipients-with.html - ASP.Net nutzt PowerShell für Anlegen einer Mailbox
http://blogs.msdn.com/akashb/archive/2009/01/30/howto-using-PowerShell-in-asp-net-net-framework-2-0.aspx - Active Directory Administrative Center für Windows Server 2008
R2 implemented using ADPowerShell!
http://blogs.msdn.com/adPowerShell/archive/2009/03/27/active-directory-administrative-center-for-windows-server-2008-r2-implemented-using-adPowerShell.aspx - Active Directory Administrative Center: Getting Started
http://technet.microsoft.com/en-us/library/dd560651.aspx - Packet Sniffer für PowerShell
http://blog.robbiefoust.com/?p=68 - Keith Hill Blog
http://keithhill.spaces.live.com
Umfangreiche Beschreibung zu PowerShell Schritten und im SkyDrive liegen einige interessante Samples - Peter’s PowerShell Blog (German only)
http://PowerShellcrashkurs.wordpress.com/ - PowerShell Sniplets
http://itriese.de/category/code-snippets/PowerShell/ - Quick Tip: Script Parameters in PowerShell
http://www.get-blog.com/?p=27