Comment passer plusieurs paramètres dans une fonction dans PowerShell?

438

Si j'ai une fonction qui accepte plusieurs paramètres de chaîne, le premier paramètre semble obtenir toutes les données qui lui sont affectées et les paramètres restants sont passés comme vides.

Un script de test rapide:

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

Test("ABC", "DEF")

La sortie générée est

$arg1 value: ABC DEF
$arg2 value: 

La sortie correcte doit être:

$arg1 value: ABC
$arg2 value: DEF

Cela semble être cohérent entre v1 et v2 sur plusieurs machines, donc évidemment, je fais quelque chose de mal. Quelqu'un peut-il indiquer exactement quoi?

Nasir
la source
3
Vous appelez juste comme ceci:Test "ABC" "DEF"
Ranadip Dutta

Réponses:

576

Les paramètres des appels aux fonctions dans PowerShell (toutes les versions) sont séparés par des espaces et non par des virgules . En outre, les parenthèses sont entièrement inutiles et provoqueront une erreur d'analyse dans PowerShell 2.0 (ou version ultérieure) si Set-StrictModeest actif. Les arguments entre parenthèses sont utilisés dans les méthodes .NET uniquement.

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

ps> foo 1 2 3
a: 1; b: 2; c: 3
x0n
la source
20
La chose la plus importante qui a finalement aidé à «coller» ceci dans mon esprit est la dernière phrase: «Les arguments entre parenthèses sont utilisés uniquement dans les méthodes .NET.
Ashley
1
Je préfère utiliser les parenthèses et les virgules séparées .. est-il possible de le faire en PowerShell?
sam yi
8
@samyi Non. Passer a (1,2,3)à une fonction est effectivement traité comme un tableau; un seul argument. Si vous souhaitez utiliser des arguments de style de méthode OO, utilisez les modules:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n
4
Powershellest un langage shell et il est courant que les langages shell utilisent des espaces comme séparateur de jetons. Je ne dirais pas Powershellest d' être différent ici, il est juste en ligne avec d' autres coquilles par défaut du système comme cmd, sh, bash, etc.
Bender le plus grand
270

La bonne réponse a déjà été fournie, mais ce problème semble suffisamment répandu pour justifier certains détails supplémentaires pour ceux qui souhaitent comprendre les subtilités.

J'aurais ajouté cela juste en tant que commentaire, mais je voulais inclure une illustration - je l'ai arraché de mon tableau de référence rapide sur les fonctions PowerShell. Cela suppose que la signature de la fonction f est f($a, $b, $c):

Pièges de syntaxe d'un appel de fonction

Ainsi, on peut appeler une fonction avec des paramètres positionnels séparés par des espaces ou des paramètres nommés indépendants de l'ordre . Les autres pièges révèlent que vous devez être conscient des virgules, des parenthèses et des espaces blancs.

Pour en savoir plus, consultez mon article Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters . L'article contient également un lien vers la référence rapide / graphique mural.

Michael Sorens
la source
4
L'explication avec la syntaxe la plus verbeuse appelant chaque paramètre et lui assignant une valeur l'a vraiment cimenté. Merci!
ConstantineK
7
Merci, ça me rendait mental de ne pas comprendre ce que je faisais mal. Quand je l'ai finalement bien fait, j'avais faim d'explications sur ce comportement.
BSAFH
1
Merci d'avoir posté ceci comme réponse. Savoir pourquoi quelque chose de mal est tout aussi important que ce qui ne va pas.
Gavin Ward
4
C'est une bien meilleure réponse. Cela devrait être plus haut.
Mark Bertenshaw
53

Il y a de bonnes réponses ici, mais je voulais souligner quelques autres choses. Les paramètres de fonction sont en fait un endroit où PowerShell brille. Par exemple, vous pouvez avoir des paramètres nommés ou positionnels dans des fonctions avancées comme ceci:

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

Ensuite, vous pouvez soit l'appeler en spécifiant le nom du paramètre, soit utiliser des paramètres positionnels, car vous les avez définis explicitement. Donc, l'un ou l'autre fonctionnerait:

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

Le premier exemple fonctionne même s'il Nameest fourni en second, car nous avons explicitement utilisé le nom du paramètre. Le deuxième exemple fonctionne cependant en fonction de la position, doncName devrait donc être le premier. Lorsque cela est possible, j'essaie toujours de définir des positions afin que les deux options soient disponibles.

PowerShell a également la possibilité de définir des ensembles de paramètres. Il utilise cela à la place de la surcharge de méthode, et est encore très utile:

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
    )
}

Maintenant, la fonction prendra un nom ou un identifiant, mais pas les deux. Vous pouvez les utiliser de manière positionnelle ou par nom. Puisqu'ils sont d'un type différent, PowerShell le découvrira. Donc, tout cela fonctionnerait:

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

Vous pouvez également affecter des paramètres supplémentaires aux différents jeux de paramètres. (C'était évidemment un exemple assez basique.) À l'intérieur de la fonction, vous pouvez déterminer quel ensemble de paramètres a été utilisé avec la propriété $ PsCmdlet.ParameterSetName. Par exemple:

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

Ensuite, sur une note connexe, il y a également la validation des paramètres dans PowerShell. C'est l'une de mes fonctionnalités PowerShell préférées, et cela rend le code à l'intérieur de vos fonctions très propre. Il existe de nombreuses validations que vous pouvez utiliser. Quelques exemples sont:

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

Dans le premier exemple, ValidatePattern accepte une expression régulière qui garantit que le paramètre fourni correspond à ce que vous attendez. Si ce n'est pas le cas, une exception intuitive est levée, vous indiquant exactement ce qui ne va pas. Donc, dans cet exemple, «Quelque chose» fonctionnerait bien, mais «Summer» ne passerait pas la validation.

ValidateRange garantit que la valeur du paramètre se situe entre la plage attendue pour un entier. Donc 10 ou 99 fonctionneraient, mais 101 lèverait une exception.

Un autre utile est ValidateSet, qui vous permet de définir explicitement un tableau de valeurs acceptables. Si quelque chose d'autre est entré, une exception sera levée. Il y en a d'autres aussi, mais le plus utile est probablement ValidateScript. Cela prend un bloc de script qui doit être évalué à $ true, donc le ciel est la limite. Par exemple:

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

Dans cet exemple, nous sommes assurés non seulement que $ Path existe, mais qu'il s'agit d'un fichier (par opposition à un répertoire) et possède une extension .csv. ($ _ fait référence au paramètre, à l'intérieur de votre bloc de script.) Vous pouvez également passer des blocs de script à plusieurs lignes beaucoup plus grands si ce niveau est requis, ou utiliser plusieurs blocs de script comme je l'ai fait ici. Il est extrêmement utile et offre de belles fonctions propres et des exceptions intuitives.

user2233949
la source
3
+1 pour démontrer My_Function -NamedParamater "ParamValue"le style d'appel de fonction. Il s'agit d'un modèle que davantage de code de script PS devrait suivre pour plus de lisibilité.
Mister_Tom
46

Vous appelez des fonctions PowerShell sans les parenthèses et sans utiliser la virgule comme séparateur. Essayez d'utiliser:

test "ABC" "DEF"

Dans PowerShell, la virgule (,) est un opérateur de tableau, par exemple

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

Il définit $aun tableau avec trois valeurs.

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

Test "ABC" "DEF"
John B
la source
11

Si vous êtes un développeur C # / Java / C ++ / Ruby / Python / Pick-A-Language-From-This-Century et que vous souhaitez appeler votre fonction avec des virgules, car c'est ce que vous avez toujours fait, alors vous avez besoin de quelque chose comme ça:

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

Appelez maintenant:

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

et vous verrez

arg1 = ABC, and arg2 = DEF
Ryan Shillington
la source
Java, C ++, Ruby et Python ne sont pas de ce siècle (seulement C #), présumant le calendrier grégorien (bien que certains aient évolué plus que d'autres).
Peter Mortensen
Il h. @PeterMortensen votre argument est que je devrais dire "Choisissez une langue de ce siècle ou du dernier"? :-)
Ryan Shillington
10

Si vous ne savez pas (ou ne vous souciez pas) du nombre d'arguments que vous passerez à la fonction, vous pouvez également utiliser une approche très simple comme;

Code :

function FunctionName()
{
    Write-Host $args
}

Cela imprimerait tous les arguments. Par exemple:

FunctionName a b c 1 2 3

Production

a b c 1 2 3

Je trouve cela particulièrement utile lors de la création de fonctions qui utilisent des commandes externes qui pourraient avoir de nombreux paramètres différents (et facultatifs), mais s'appuie sur cette commande pour fournir des commentaires sur les erreurs de syntaxe, etc.

Voici un autre exemple du monde réel (création d'une fonction pour la commande tracert, que je déteste devoir me souvenir du nom tronqué);

Code :

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

Si tu essayes:

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

vous obtenez:

$arg1 value: ABC GHI
$arg2 value: DEF

Vous voyez donc que les parenthèses séparent les paramètres


Si tu essayes:

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

vous obtenez:

$arg1 value: ABC
$arg2 value: DEF

Vous pouvez maintenant trouver une utilité immédiate des parenthèses - un espace ne deviendra pas un séparateur pour le paramètre suivant - à la place, vous avez une fonction eval.

RaSor
la source
4
Les parents ne séparent pas les paramètres. Ils définissent le tableau.
2013
1
Les parents ne définissent pas un tableau, ils définissent un groupe, que PowerShell peut interpréter comme un tableau. Les tableaux sont définis par le symbole ( @) avant les premiers paren, comme ce tableau vide: @(); ou ce tableau avec deux numéros: @(1, 2).
VertigoRay
5

Comme il s'agit d'une question fréquemment consultée, je tiens à mentionner qu'une fonction PowerShell doit utiliser des verbes approuvés ( Verb-Noun comme nom de fonction). La partie verbale du nom identifie l'action que l'applet de commande effectue. La partie nominale du nom identifie l'entité sur laquelle l'action est effectuée. Cette règle simplifie l'utilisation de vos applets de commande pour les utilisateurs avancés de PowerShell.

De plus, vous pouvez spécifier des choses comme si le paramètre est obligatoire et la position du paramètre:

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"
}

Pour passer le paramètre à la fonction, vous pouvez soit utiliser la position :

Test-Script "Hello" "World"

Ou toi spécifiez le nom du paramètre :

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

Vous n'utilisez pas de parenthèses comme vous le faites lorsque vous appelez une fonction dans C #.


Je recommanderais de toujours passer les noms de paramètres lorsque vous utilisez plusieurs paramètres, car cela est plus lisible .

Martin Brandl
la source
Pour info, le lien vers la liste des verbes approuvés ne fonctionne plus, mais peut maintenant être trouvé ici - docs.microsoft.com/en-us/powershell/developer/cmdlet/…
Keith Langmead
@KeithLangmead Merci Keith, j'ai également mis à jour ma réponse.
Martin Brandl
1
"Verb-Noun" comme dans le verbe et le nom en majuscule? Peut-être changer la réponse pour être plus explicite à ce sujet?
Peter Mortensen
@PeterMortensen Merci d'avoir mentionné cela. J'ai mis à jour ma réponse.
Martin Brandl
1
pensez à exposer une Get-Nodeapplet de commande. Il serait clair pour nous que nous devons invoquer Get-Node, non Retrieve-Node, ni Receive-Node, ni .....
Martin Brandl
4

Je ne sais pas ce que vous faites avec la fonction, mais jetez un œil à l'utilisation du mot-clé 'param'. Il est un peu plus puissant pour passer des paramètres dans une fonction et la rend plus conviviale. Vous trouverez ci-dessous un lien vers un article trop complexe de Microsoft à ce sujet. Ce n'est pas aussi compliqué que l'article le donne.

Utilisation des paramètres

Aussi, voici un exemple d' une question sur ce site:

Vérifiez-le.

Rodney Fisk
la source
Merci d'avoir répondu. Cependant, j'avais des problèmes lors de l'appel de la fonction. Peu importe si la fonction a été déclarée avec ou sans param.
Nasir
3
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC") ("DEF")
kleopatra
la source
Le résultat sortira comme il se doit:
2

J'ai déclaré ce qui suit plus tôt:

Le problème courant est l'utilisation de la forme singulière $arg, qui est incorrecte. Il doit toujours être pluriel comme$args .

Le problème n'est pas ça. En réalité,$arg peut être autre chose. Le problème était l'utilisation de la virgule et des parenthèses.

J'exécute le code suivant qui a fonctionné et la sortie suit:

Code:

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

Test "ABC" "DEF"

Production:

$var1 value: ABC
$var2 value: DEF
Eric
la source
4
Merci mon ami, cependant, vous avez deux ans de retard :-) Les trois premières réponses ici ont suffisamment résolu le problème. Puis-je suggérer de passer à la section Sans réponse et d'essayer certaines de ces questions?
Nasir
2
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

Ceci est un bon params déclaration .

Voir about_Functions_Advanced_Parameters .

Et cela fonctionne en effet.

Serhii Kimlyk
la source
1

Vous pouvez également transmettre des paramètres dans une fonction comme celle-ci:

function FunctionName()
{
    Param ([string]$ParamName);
    # Operations
}
Kaushal Khamar
la source
3
Ce serait définir des paramètres pour une fonction, la question d'origine était de savoir comment spécifier des paramètres lorsque vous appelez la fonction.
Nasir
1

Je ne le vois pas mentionné ici, mais répartir vos arguments est une alternative utile et devient particulièrement utile si vous construisez les arguments d'une commande dynamiquement (par opposition à l'utilisationInvoke-Expression ). Vous pouvez répartir des tableaux pour les arguments positionnels et des tables de hachage pour les arguments nommés. Voici quelques exemples:

Splat avec des tableaux (arguments positionnels)

Test de connexion avec des arguments positionnels

Test-Connection www.google.com localhost

Avec éclaboussure de tableau

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

Notez qu'en éclaboussant, nous référençons la variable éclaboussée avec un @au lieu d'un $. Il en va de même lorsque vous utilisez un Hashtable pour splatter également.

Splat With Hashtable (arguments nommés)

Test de connexion avec des arguments nommés

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

Avec éclaboussures de table de hachage

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

Splat arguments positionnels et nommés simultanément

Test-Connection avec les arguments positionnels et nommés

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

Splatting Array and Hashtables Together

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
Bender le plus grand
la source