web-dev-qa-db-fra.com

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

Si j'ai une fonction qui accepte plus d'un paramètre 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 devrait ê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 préciser exactement quoi?

381
Nasir

Les paramètres dans les appels de fonctions dans PowerShell (toutes versions) sont séparés par des espaces et non par des virgules . De plus, les parenthèses ne sont absolument pas nécessaires et provoqueront une erreur d'analyse dans PowerShell 2.0 (ou version ultérieure) si Set-StrictMode est actif. Les arguments entre parenthèses sont utilisés uniquement dans les méthodes .NET.

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

ps> foo 1 2 3
a: 1; b: 2; c: 3
_
515
x0n

La réponse correcte a déjà été fournie, mais ce problème semble suffisamment répandu pour justifier des détails supplémentaires pour ceux qui souhaitent comprendre les subtilités. J'aurais ajouté ceci simplement à titre de commentaire, mais je voulais inclure une illustration - je l'ai déchiré de mon tableau de référence rapide sur les fonctions PowerShell. Ceci suppose que la signature de la fonction f est f($a, $b, $c):

syntax pitfalls of a function call

Ainsi, on peut appeler une fonction avec positional = = paramètres == ou indépendant de l'ordre nommé paramètres. Les autres pièges indiquent que vous devez connaître les virgules, les parenthèses, et espaces.

Pour en savoir plus, voir mon article Down the Rabbit Hole: une étude sur les pipelines, fonctions et paramètres de PowerShell vient d'être publié sur Simple-Talk.com. L'article contient également un lien vers la référence rapide/le tableau mural.

240
Michael Sorens

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 $a sur un tableau avec trois valeurs.

43
Todd

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

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

Vous pouvez ensuite l'appeler en spécifiant le nom du paramètre ou simplement utiliser des paramètres de position, puisque vous les avez explicitement définis. Donc, l’un ou l’autre pourrait fonctionner:

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

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

PowerShell a également la possibilité de définir des jeux de paramètres. Il utilise ceci à 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 soit un nom, soit un identifiant, mais pas les deux. Vous pouvez les utiliser par leur position ou par leur nom. Comme ils sont d'un type différent, PowerShell le trouvera. 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 jeu 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 parallèle, il existe également une validation de paramètre dans PowerShell. C'est l'une de mes fonctionnalités préférées de PowerShell et rend le code 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 assure que le paramètre fourni correspond à ce que vous attendez. Si ce n'est pas le cas, une exception intuitive est émise, vous indiquant exactement ce qui ne va pas. Donc, dans cet exemple, "Quelque chose" fonctionnerait bien, mais "Summer" ne passerait pas la validation.

ValidateRange s'assure que la valeur du paramètre est comprise dans la plage attendue pour un entier. Donc 10 ou 99 fonctionneraient, mais 101 lancerait une exception.

ValidateSet est un autre outil utile, 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 en existe d’autres également, mais probablement le plus utile l’un est 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 que non seulement $ Path existe, mais qu’il s’agit d’un fichier (par opposition à un répertoire) et d’une extension .csv. ($ _ fait référence au paramètre lorsque vous vous trouvez dans votre scriptblock.) Vous pouvez également transmettre des blocs de script multilignes beaucoup plus volumineux si ce niveau est requis, ou utiliser plusieurs scriptblocks comme je l'ai fait ici. C'est extrêmement utile et permet des fonctions propres et des exceptions intuitives.

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

Test "ABC" "DEF"
13
John B

Si vous êtes un développeur C #/Java/C++/Ruby/Python/Choisissez-un-langage-de-ce-siècle et vous voulez appeler votre fonction avec des virgules, parce que 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
10
Ryan Shillington

Si vous ne savez pas (ou ne vous souciez pas) combien d'arguments vous allez transmettre à la fonction, vous pouvez également utiliser une approche très simple comme:

Code:

function FunctionName()
{
    Write-Host $args
}

Cela afficherait tous les arguments. Par exemple:

FunctionName a b c 1 2 3

sortie

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 qui reposent sur cette commande pour fournir un retour d'information sur les erreurs de syntaxe, etc.

Voici un autre exemple concret (création d'une fonction dans la commande tracert, que je déteste devoir rappeler le nom tronqué);

Code:

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

Si tu essayes:

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

vous obtenez:

$arg1 value: ABC GHI
$arg2 value: DEF

de sorte que vous voyez que la parenthèse sépare 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 de la parenthèse - un espace ne deviendra pas un séparateur pour le paramètre suivant - vous avez plutôt une fonction eval.

5
RaSor

Je ne sais pas ce que vous faites avec la fonction, mais jetez un coup d'œ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 dit. Param Usage

Aussi, voici un exemple tiré d'un fil sur ce site:

Vérifiez-le.

3
Rodney Fisk

Comme il s'agit d'une question fréquemment consultée, je souhaite mentionner qu'une fonction PowerShell doit utiliser verbes approuvés ( verbe-nom comme nom de la fonction). Vous pouvez également spécifier des éléments tels que 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 transmettre le paramètre à la fonction, vous pouvez utiliser la position :

Test-Script "Hello" "World"

Ou vous spécifiez le paramètre name :

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 transmettre les noms de paramètre lorsque vous utilisez plus d'un paramètre, car il s'agit de plus lisible .

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

Test("ABC") ("DEF")
2
user3222697
Function Test {
 Param([string]$arg1, [string]$arg2)
    Write-Host $arg1
    Write-Host $arg2
}

C'est la déclaration de paramètres appropriée https://technet.Microsoft.com/en-us/library/dd347600.aspx

Et ça marche vraiment

1
Serhii Kimlyk

Je déclare ce qui suit plus tôt:

Le problème commun utilise la forme singulière $arg, qui est incorrecte.
Il devrait toujours être au pluriel comme $args.

Le problème n'est pas ça.
En fait, $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 le résultat est le suivant:

Code:

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

Test "ABC" "DEF"

Sortie:

valeur $ var1: ABC valeur var2 $: DEF

1
Eric

Je ne le vois pas mentionné ici, mais éclaboussures vos arguments sont une alternative utile et deviennent particulièrement utiles si vous générez les arguments d'une commande de manière dynamique (par opposition à l'utilisation de Invoke-Expression ). Vous pouvez créer des tableaux pour les arguments de position et des tables de hachage pour les arguments nommés. Voici quelques exemples:

Splat With Arrays (Arguments de position)

Test-Connection avec des arguments positionnels

Test-Connection www.google.com localhost

Avec tableau éclaboussant

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

Notez que lors du splatting, nous référençons la variable splatted avec un @ au lieu d'un $. Il en va de même lorsque vous utilisez une table de hachage pour splat.

Splat avec table de hachage (arguments nommés)

Test-Connection avec des arguments nommés

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

Avec Hashtable Splatting

$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 et Hashtables ensemble

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
0

Vous pouvez passer paramètres dans fonction comme ceci également.

function FunctionName()
{
    Param ([string]$ParamName);
    #Operations
}
0
Kaushal Khamar