Wie übergebe ich mehrere Parameter an eine Funktion in PowerShell?

435

Wenn ich eine Funktion habe, die mehr als einen Zeichenfolgenparameter akzeptiert, scheint der erste Parameter alle ihm zugewiesenen Daten zu erhalten, und die verbleibenden Parameter werden als leer übergeben.

Ein schnelles Testskript:

Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC", "DEF")

Die erzeugte Ausgabe ist

$arg1 value: ABC DEF
$arg2 value: 

Die richtige Ausgabe sollte sein:

$arg1 value: ABC
$arg2 value: DEF

Dies scheint zwischen v1 und v2 auf mehreren Computern konsistent zu sein, daher mache ich offensichtlich etwas falsch. Kann jemand genau darauf hinweisen, was?

Nasir
quelle
2
Sie rufen einfach so an:Test "ABC" "DEF"
Ranadip Dutta

Antworten:

574

Parameter bei Aufrufen von Funktionen in PowerShell (alle Versionen) sind durch Leerzeichen und nicht durch Kommas getrennt . Außerdem sind die Klammern völlig unnötig und verursachen einen Analysefehler in PowerShell 2.0 (oder höher), wenn er Set-StrictModeaktiv ist. Argumente in Klammern werden nur in .NET-Methoden verwendet.

function foo($a, $b, $c) {
   "a: $a; b: $b; c: $c"
}

ps> foo 1 2 3
a: 1; b: 2; c: 3
x0n
quelle
20
Das Wichtigste, was mir letztendlich geholfen hat, dies in Erinnerung zu behalten, ist der letzte Satz: "Argumente in Klammern werden nur in .NET-Methoden verwendet."
Ashley
1
Ich bevorzuge die Verwendung von Klammern und Komma getrennt. Ist dies in Powershell möglich?
Sam Yi
8
@samyi Nein. Das Übergeben von a (1,2,3)an eine Funktion wird effektiv als Array behandelt. ein einziges Argument. Wenn Sie Argumente im OO-Methodenstil verwenden möchten, verwenden Sie die folgenden Module:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n
4
Powershellist eine Shell-Sprache und es ist üblich, dass Shell-Sprachen Leerzeichen als Token-Trennzeichen verwenden. Ich würde nicht sagen , Powershellist anders , hier zu sein, es ist direkt im Einklang mit anderen System - Standard - Shells wie cmd, sh, bashusw.
Bender der größten
269

Die richtige Antwort wurde bereits gegeben, aber dieses Problem scheint weit genug verbreitet zu sein, um einige zusätzliche Details für diejenigen zu rechtfertigen, die die Feinheiten verstehen möchten.

Ich hätte dies nur als Kommentar hinzugefügt, aber ich wollte eine Illustration hinzufügen - ich habe dies aus meiner Kurzreferenztabelle zu PowerShell-Funktionen herausgerissen. Dies setzt voraus, dass die Signatur der Funktion f lautet f($a, $b, $c):

Syntax-Fallstricke eines Funktionsaufrufs

Somit kann man eine Funktion mit durch Leerzeichen getrennten Positionsparametern oder auftragsunabhängigen benannten Parametern aufrufen . Die anderen Fallstricke zeigen, dass Sie Kommas, Klammern und Leerzeichen kennen müssen.

Weitere Informationen finden Sie in meinem Artikel Down the Rabbit Hole: Eine Studie zu PowerShell-Pipelines, -Funktionen und -Parametern . Der Artikel enthält auch einen Link zur Kurzreferenz / Wandkarte.

Michael Sorens
quelle
4
Die Erklärung mit der ausführlicheren Syntax, die jeden Parameter aufruft und ihm einen Wert zuweist, hat ihn wirklich zementiert. Danke!
ConstantineK
7
Danke, es hat mich mental dazu gebracht, nicht zu verstehen, was ich falsch gemacht habe. Als ich es endlich richtig machte, war ich hungrig nach einer Erklärung für dieses Verhalten.
BSAFH
1
Vielen Dank, dass Sie dies als Antwort gepostet haben. Zu wissen, warum etwas nicht stimmt, ist genauso wichtig wie was nicht.
Gavin Ward
4
Dies ist eine weitaus bessere Antwort. Es sollte weiter oben sein.
Mark Bertenshaw
52

Hier gibt es einige gute Antworten, aber ich wollte auf ein paar andere Dinge hinweisen. Funktionsparameter sind tatsächlich ein Ort, an dem PowerShell glänzt. Beispielsweise können Sie in erweiterten Funktionen wie folgt entweder benannte oder Positionsparameter haben:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [int] $Id
    )
}

Dann können Sie es entweder durch Angabe des Parameternamens aufrufen oder einfach Positionsparameter verwenden, da Sie diese explizit definiert haben. Beides würde also funktionieren:

Get-Something -Id 34 -Name "Blah"
Get-Something "Blah" 34

Das erste Beispiel funktioniert, obwohl Namees als zweites bereitgestellt wird, da wir den Parameternamen explizit verwendet haben. Das zweite Beispiel basiert jedoch auf der Position und muss daher Namedas erste sein. Wenn möglich, versuche ich immer, Positionen zu definieren, damit beide Optionen verfügbar sind.

PowerShell kann auch Parametersätze definieren. Es verwendet dies anstelle der Methodenüberladung und ist wiederum sehr nützlich:

function Get-Something
{
    [CmdletBinding(DefaultParameterSetName='Name')]
    Param
    (
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Name')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=0, ParameterSetName='Id')]
         [int] $Id
    )
}

Jetzt nimmt die Funktion entweder einen Namen oder eine ID an, aber nicht beide. Sie können sie positionell oder namentlich verwenden. Da es sich um einen anderen Typ handelt, wird PowerShell dies herausfinden. All dies würde also funktionieren:

Get-Something "some name"
Get-Something 23
Get-Something -Name "some name"
Get-Something -Id 23

Sie können den verschiedenen Parametersätzen auch zusätzliche Parameter zuweisen. (Das war offensichtlich ein ziemlich einfaches Beispiel.) Innerhalb der Funktion können Sie bestimmen, welcher Parametersatz mit der Eigenschaft $ PsCmdlet.ParameterSetName verwendet wurde. Zum Beispiel:

if($PsCmdlet.ParameterSetName -eq "Name")
{
    Write-Host "Doing something with name here"
}

In einer verwandten Randnotiz gibt es dann auch eine Parameterüberprüfung in PowerShell. Dies ist eine meiner Lieblingsfunktionen von PowerShell und macht den Code in Ihren Funktionen sehr sauber. Es gibt zahlreiche Validierungen, die Sie verwenden können. Einige Beispiele sind:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidatePattern('^Some.*')]
         [string] $Name,
         [Parameter(Mandatory=$true, Position=1)]
         [ValidateRange(10,100)]
         [int] $Id
    )
}

Im ersten Beispiel akzeptiert ValidatePattern einen regulären Ausdruck, der sicherstellt, dass der angegebene Parameter Ihren Erwartungen entspricht. Wenn dies nicht der Fall ist, wird eine intuitive Ausnahme ausgelöst, die Ihnen genau sagt, was falsch ist. In diesem Beispiel würde "Etwas" gut funktionieren, aber "Sommer" würde die Validierung nicht bestehen.

ValidateRange stellt sicher, dass der Parameterwert zwischen dem Bereich liegt, den Sie für eine Ganzzahl erwarten. Also würden 10 oder 99 funktionieren, aber 101 würde eine Ausnahme auslösen.

Ein weiteres nützliches ist ValidateSet, mit dem Sie ein Array akzeptabler Werte explizit definieren können. Wenn etwas anderes eingegeben wird, wird eine Ausnahme ausgelöst. Es gibt auch andere, aber das wahrscheinlich nützlichste ist ValidateScript. Dies erfordert einen Skriptblock, der mit $ true ausgewertet werden muss, sodass der Himmel die Grenze darstellt. Zum Beispiel:

function Get-Something
{
    Param
    (
         [Parameter(Mandatory=$true, Position=0)]
         [ValidateScript({ Test-Path $_ -PathType 'Leaf' })]
         [ValidateScript({ (Get-Item $_ | select -Expand Extension) -eq ".csv" })]
         [string] $Path
    )
}

In diesem Beispiel wird nicht nur versichert, dass $ Path vorhanden ist, sondern dass es sich um eine Datei handelt (im Gegensatz zu einem Verzeichnis) und die Erweiterung .csv hat. ($ _ bezieht sich auf den Parameter in Ihrem Skriptblock.) Sie können auch viel größere mehrzeilige Skriptblöcke übergeben, wenn diese Ebene erforderlich ist, oder mehrere Skriptblöcke verwenden, wie ich es hier getan habe. Es ist äußerst nützlich und sorgt für schöne, saubere Funktionen und intuitive Ausnahmen.

user2233949
quelle
3
+1 zur Demonstration des My_Function -NamedParamater "ParamValue"Funktionsaufrufstils. Dies ist ein Muster, dem mehr PS-Skriptcode zur besseren Lesbarkeit folgen sollte.
Mister_Tom
45

Sie rufen PowerShell-Funktionen ohne Klammern und ohne Verwendung des Kommas als Trennzeichen auf. Versuchen Sie es mit:

test "ABC" "DEF"

In PowerShell ist das Komma (,) ein Array-Operator, z

$a = "one", "two", "three"

Es wird $aein Array mit drei Werten festgelegt.

Todd
quelle
16
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test "ABC" "DEF"
John B.
quelle
11

Wenn Sie ein C # / Java / C ++ / Ruby / Python / Pick-A-Language-From-This-Century-Entwickler sind und Ihre Funktion mit Kommas aufrufen möchten, weil Sie dies immer getan haben, brauchen Sie etwas so was:

$myModule = New-Module -ascustomobject { 
    function test($arg1, $arg2) { 
        echo "arg1 = $arg1, and arg2 = $arg2"
    }
}

Ruf jetzt an:

$myModule.test("ABC", "DEF")

und du wirst sehen

arg1 = ABC, and arg2 = DEF
Ryan Shillington
quelle
Java, C ++, Ruby und Python stammen nicht aus diesem Jahrhundert (nur C #), vorausgesetzt der Gregorianische Kalender (obwohl sich einige mehr als andere entwickelt haben).
Peter Mortensen
Heh. @PeterMortensen Ihr Argument ist, dass ich sagen sollte "Wählen Sie eine Sprache aus diesem oder dem letzten Jahrhundert"? :-)
Ryan Shillington
10

Wenn Sie nicht wissen (oder sich nicht darum kümmern), wie viele Argumente Sie an die Funktion übergeben, können Sie auch einen sehr einfachen Ansatz verwenden, z.

Code :

function FunctionName()
{
    Write-Host $args
}

Das würde alle Argumente ausdrucken. Zum Beispiel:

FunctionName a b c 1 2 3

Ausgabe

a b c 1 2 3

Ich finde dies besonders nützlich, wenn ich Funktionen erstelle, die externe Befehle verwenden, die viele verschiedene (und optionale) Parameter haben können, sich aber auf diesen Befehl stützen, um Feedback zu Syntaxfehlern usw. zu geben.

Hier ist ein weiteres Beispiel aus der Praxis (Erstellen einer Funktion für den Befehl tracert, die ich nicht gerne an den abgeschnittenen Namen erinnern muss).

Code :

Function traceroute
{
    Start-Process -FilePath "$env:systemroot\system32\tracert.exe" -ArgumentList $args -NoNewWindow
}
Draino
quelle
7

Wenn du es versuchst:

PS > Test("ABC", "GHI") ("DEF")

du erhältst:

$arg1 value: ABC GHI
$arg2 value: DEF

Sie sehen also, dass die Klammern die Parameter trennen


Wenn du es versuchst:

PS > $var = "C"
PS > Test ("AB" + $var) "DEF"

du erhältst:

$arg1 value: ABC
$arg2 value: DEF

Jetzt könnten Sie eine unmittelbare Nützlichkeit der Klammern finden - ein Leerzeichen wird nicht zum Trennzeichen für den nächsten Parameter - stattdessen haben Sie eine Auswertungsfunktion.

RaSor
quelle
4
Parens trennen keine Parameter. Sie definieren Array.
12.
1
Parens definieren kein Array, sondern eine Gruppe, die Powershell als Array interpretieren kann. Arrays werden @wie dieses leere Array mit einem at-Zeichen ( ) vor dem führenden Paren definiert : @(); oder dieses Array mit zwei Zahlen : @(1, 2).
VertigoRay
5

Da dies eine häufig gestellte Frage ist, möchte ich erwähnen, dass eine PowerShell-Funktion genehmigte Verben verwenden sollte ( Verb-Nomen als Funktionsname). Der Verbteil des Namens gibt die Aktion an, die das Cmdlet ausführt. Der Substantivteil des Namens identifiziert die Entität, für die die Aktion ausgeführt wird. Diese Regel vereinfacht die Verwendung Ihrer Cmdlets für fortgeschrittene PowerShell-Benutzer.

Sie können auch festlegen, ob der Parameter obligatorisch ist und an welcher Position der Parameter steht:

function Test-Script
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory=$true, Position=0)]
        [string]$arg1,

        [Parameter(Mandatory=$true, Position=1)]
        [string]$arg2
    )

    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Um den Parameter an die Funktion zu übergeben, können Sie entweder die Position verwenden :

Test-Script "Hello" "World"

Oder Sie geben die Parameternamen :

Test-Script -arg1 "Hello" -arg2 "World"

Sie verwenden keine Klammern wie beim Aufrufen einer Funktion in C #.


Ich würde empfehlen, immer die Parameternamen zu übergeben, wenn mehr als ein Parameter verwendet wird, da dies besser lesbar ist .

Martin Brandl
quelle
Zu Ihrer Information, der
Keith Langmead
@KeithLangmead Danke Keith, ich habe auch meine Antwort aktualisiert.
Martin Brandl
1
"Verb-Nomen" wie in Verb und Nomen großgeschrieben? Vielleicht ändern Sie die Antwort, um es expliziter zu machen?
Peter Mortensen
@ PeterMortensen Danke, dass du das erwähnt hast. Ich habe meine Antwort aktualisiert.
Martin Brandl
1
Betrachten Sie ein Get-NodeCmdlet. Es wäre für uns klar, dass wir zu berufen haben Get-Node, nicht Retrieve-Node, noch Receive-Node, noch .....
Martin Brandl
4

Ich weiß nicht, was Sie mit der Funktion machen, aber schauen Sie sich das Schlüsselwort 'param' an. Es ist viel leistungsfähiger, um Parameter an eine Funktion zu übergeben, und macht es benutzerfreundlicher. Unten finden Sie einen Link zu einem übermäßig komplexen Artikel von Microsoft darüber. Es ist nicht so kompliziert, wie der Artikel es klingen lässt.

Param Verwendung

Hier ist auch ein Beispiel aus einer Frage auf dieser Site:

Hör zu.

Rodney Fisk
quelle
Danke für die Antwort. Ich hatte jedoch Probleme beim Aufrufen der Funktion. Es war egal, ob die Funktion mit oder ohne param deklariert wurde.
Nasir
3
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC") ("DEF")
Kleopatra
quelle
Das Ergebnis wird so herauskommen, wie es sollte:
2

Ich habe vorhin Folgendes angegeben:

Das häufigste Problem ist die Verwendung der Singularform $arg, die falsch ist. Es sollte immer Plural sein als $args.

Das Problem ist nicht das. In der Tat $argkann alles andere sein. Das Problem war die Verwendung des Kommas und der Klammern.

Ich führe den folgenden Code aus, der funktioniert hat, und die Ausgabe folgt:

Code:

Function Test([string]$var1, [string]$var2)
{
    Write-Host "`$var1 value: $var1"
    Write-Host "`$var2 value: $var2"
}

Test "ABC" "DEF"

Ausgabe:

$var1 value: ABC
$var2 value: DEF
Eric
quelle
4
Vielen Dank, mein Freund, aber Sie sind ein paar Jahre zu spät :-) Die drei besten Antworten hier hatten das Problem ausreichend angesprochen. Darf ich vorschlagen, zum unbeantworteten Bereich zu gehen und einige dieser Fragen zu beantworten?
Nasir
2
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

Dies ist eine ordnungsgemäße paramsErklärung.

Siehe about_Functions_Advanced_Parameters .

Und es funktioniert tatsächlich.

Serhii Kimlyk
quelle
1

Sie können Parameter in einer Funktion wie dieser auch übergeben:

function FunctionName()
{
    Param ([string]$ParamName);
    # Operations
}
Kaushal Khamar
quelle
3
Das würde Parameter für eine Funktion definieren. Die ursprüngliche Frage war, wie Parameter beim Aufrufen der Funktion angegeben werden.
Nasir
1

Ich sehe es hier nicht erwähnt, aber das Aufteilen Ihrer Argumente ist eine nützliche Alternative und wird besonders nützlich, wenn Sie die Argumente für einen Befehl dynamisch erstellen (im Gegensatz zur Verwendung Invoke-Expression). Sie können mit Arrays für Positionsargumente und Hashtabellen für benannte Argumente splat. Hier sind einige Beispiele:

Splat mit Arrays (Positionsargumente)

Testverbindung mit Positionsargumenten

Test-Connection www.google.com localhost

Mit Array-Splatting

$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentArray

Beachten Sie, dass wir beim Splattern auf die splattierte Variable mit einem @anstelle von a verweisen $. Dies gilt auch für die Verwendung eines Hashtables zum Splattern.

Splat With Hashtable (benannte Argumente)

Testverbindung mit benannten Argumenten

Test-Connection -ComputerName www.google.com -Source localhost

Mit Hashtable Splatting

$argumentHash = @{
  ComputerName = 'www.google.com'
  Source = 'localhost'
}
Test-Connection @argumentHash

Positive und benannte Argumente gleichzeitig teilen

Testverbindung mit Positions- und benannten Argumenten

Test-Connection www.google.com localhost -Count 1

Array und Hashtables zusammen teilen

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
Bender der Größte
quelle