Как передать несколько параметров в функцию в PowerShell?

Если у меня есть функция, которая принимает более одного строкового параметра, первый параметр, похоже, получает все данные, назначенные ему, а остальные параметры передаются как пустые.

быстрый тестовый скрипт:

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

Test("ABC", "DEF")

выход генерируется

$arg1 value: ABC DEF
$arg2 value: 

правильный вывод должен быть:

$arg1 value: ABC
$arg2 value: DEF

это, кажется, согласуется между v1 и v2 на нескольких машинах, поэтому, очевидно, я делаю что-то неправильно. Может ли кто-нибудь указать что именно?

14 ответов


параметры в вызовах функций в PowerShell (все версии) разделены пробелами, а не запятыми. Кроме того, круглые скобки совершенно излишни и вызовут ошибку синтаксического анализа в PowerShell 2.0 (или более поздней версии), если Set-StrictMode активна. Parenthesised аргументы используются .Чистая только методы.

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

ps> foo 1 2 3
a: 1; b: 2; c: 3

правильный ответ уже был предоставлен, но эта проблема кажется достаточно распространенной, чтобы гарантировать некоторые дополнительные детали для тех, кто хочет понять тонкости. Я бы добавил Это просто как комментарий, но я хотел включить иллюстрацию-я оторвал это от моей краткой справочной диаграммы по функциям PowerShell. Это предполагает, что подпись функции f является f($a, $b, $c):

syntax pitfalls of a function call

таким образом, можно вызвать функцию с пробелами позиционные параметры или порядок-независимый имени параметры. Другие подводные камни показывают, что вам нужно знать запятые, круглые скобки,и белое пространство.

для дальнейшего чтения см. мои статьи Down The Rabbit Hole: исследование трубопроводов, функций и параметров PowerShell только что опубликовано Simple-Talk.com - ... В статье также содержится ссылка на краткую справочную / настенную диаграмму.


вы вызываете функции PowerShell без скобок и без использования запятой в качестве разделителя. Попробуйте использовать:

   test "ABC" "DEF"

в PowerShell запятая (,) является оператором выбора, например,

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

она устанавливает $a в массив с тремя значениями.


некоторые хорошие ответы здесь, но я хотел указать на пару других вещей. Параметры функции на самом деле являются местом, где PowerShell сияет. Например, вы можете иметь именованные или позиционные параметры в расширенных функциях, например:

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

затем вы можете вызвать его, указав имя параметра, или вы можете просто использовать позиционные параметры, так как вы явно определили их. Так что любой из них будет работать:

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

первый пример работает, даже если имя указано вторым, потому что мы явно использовали имя параметра. Второй пример работает на основе позиции, поэтому имя должно быть первым. Когда это возможно, я всегда пытаюсь определить позиции, чтобы были доступны оба варианта.

PowerShell также имеет возможность определять наборы параметров. Он использует это вместо перегрузки метода и снова довольно полезен:

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

теперь функция будет либо принимать имя, либо идентификатор, но не оба. Вы может использовать их позиционно или по имени. Поскольку они другого типа, PowerShell это выяснит. Так что все это будет работать

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

вы также можете указать дополнительные параметры для различных наборов параметров. (Это был довольно простой пример, очевидно) внутри функции вы можете определить, какой набор параметров использовался с $PsCmdlet.Свойство ParameterSetName. Например:

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

затем, на связанной стороне Примечания, есть также проверка параметров в PowerShell. Это одна из моих любимых функций PowerShell, и она делает код внутри ваших функций очень чистым. Существует множество проверок, которые вы можете использовать. Пару примеров

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

в первом примере ValidatePattern принимает регулярное выражение, которое гарантирует, что предоставленный параметр соответствует ожидаемому. Если это не так, возникает интуитивное исключение, которое точно говорит вам, что не так. Так что в этом примере "что-то" будет работать нормально, но "лето" не пройдет проверку.

ValidateRange гарантирует, что значение параметра находится между круг вы ожидаете целое. Таким образом, 10 или 99 будут работать, но 101 вызовет исключение.

еще одним полезным является ValidateSet, который позволяет явно определить массив допустимых значений. Если введено что-то еще, будет создано исключение. Есть и другие, но, вероятно,самое полезное один ValidateScript. Это требует блок скрипта, который должен оцениваться в $true, поэтому небо является пределом. Например:

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

в этом примере мы уверены не только в том, что $Path существует, но и в том, что это файл (в отличие от каталога) и имеет .расширение csv. ($_ относится к параметру, когда внутри вашего scriptblock.) Вы также можете передать гораздо большие, многострочные блоки скриптов, если этот уровень требуется, или использовать несколько блоков скриптов, как я сделал здесь. Это чрезвычайно полезно и делает для приятных чистых функций и интуитивные исключения.


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

Test "ABC" "DEF"

Если вы разработчик C# / Java / C++ / Ruby / Python / Pick-a-Language-From-This-Century и вы хотите вызвать свою функцию запятыми, потому что это то, что вы всегда делали, тогда вам нужно что-то вроде этого:

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

звоните прямо сейчас:

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

и вы увидите

arg1 = ABC, and arg2 = DEF

если вы попробуете:

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

вы получаете:

$arg1 value: ABC GHI
$arg2 value: DEF

Итак, вы видите, что скобка разделяет параметры

если вы попробуете:

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

вы получаете:

$arg1 value: ABC
$arg2 value: DEF

теперь вы можете найти некоторую непосредственную полезность скобки-пробел не станет разделителем для следующего параметра - вместо этого у вас есть функция eval.


если вы не знаете (или не заботитесь), сколько аргументов вы будете передавать функции, вы также можете использовать очень простой подход, например;

код:

function FunctionName()
{
    Write-Host $args
}

это распечатает все аргументы. Например:

FunctionName a b c 1 2 3

выход

a b c 1 2 3

Я нахожу это особенно полезным при создании функций, которые используют внешние команды, которые могут иметь много разных (и необязательных) параметров, но полагаются на сказанное команда для обеспечения обратной связи по синтаксическим ошибкам и т. д.

вот еще один пример реального мира (создание функции для команды tracert, которую я ненавижу помнить усеченное имя);

код:

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

Я не знаю, что вы делаете с функцией, но посмотрите на использование ключевого слова "param". Это довольно немного мощнее для передачи параметров в функцию и делает ее более удобной для пользователя. Ниже приведена ссылка на чрезмерно сложную статью от Microsoft об этом. Это не так сложно, как кажется в статье. Использование Param

кроме того, вот пример нить на этом сайте:

проверить.


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

Test("ABC") ("DEF")

Я заявляю следующее ранее:

общей проблемой является использование сингулярной формы $arg, что является неверным.
Он всегда должен быть множественным как $args.

проблема не в этом.
На самом деле,$arg может быть что-нибудь еще. Проблема заключалась в использование запятой и parantheses.
Я запускаю следующий код, который работал, и вывод следующий:

код:

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

тест " ABC "" DEF"

выход:

$var1 значение: ABC $ var2 значение: DEF


Function Test {
 Param([string]$arg1, [string]$arg2)
    Write-Host $arg1
    Write-Host $arg2
}

это правильное объявление params https://technet.microsoft.com/en-us/library/dd347600.aspx

и это действительно работает


поскольку это часто просматриваемый вопрос, я хочу упомянуть, что функция PowerShell должна использовать утвержденных команд (Глагол-Существительное как имя функции). Также вы можете указать такие вещи, как, является ли параметр обязательное и позиция параметр:

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

чтобы передать параметр функции, вы можете использовать позиция:

Test-Script "Hello" "World"

или вы указать параметр имя:

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

вы не использовать скобки как вы делаете, когда вы вызываете функцию в C#.


Я бы рекомендовал всегда передать имена параметров при использовании более одного параметра, так как это больше читабельный.


вы можете передать параметры на функции такой же.

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