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

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

Быстрый тест script:

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 на нескольких машинах, поэтому, очевидно, я делаю что-то неправильно. Кто-нибудь может указать, что именно?

Ответ 1

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

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

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

Ответ 2

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

syntax pitfalls of a function call

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

Для дальнейшего чтения см. мою статью Вниз Rabbit Hole: исследование в PowerShell Pipelines, Functions и Parameters, опубликованное только на Simple-Talk. ком. В статье также содержится ссылка на краткую справочную/настенную диаграмму.

Ответ 3

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

test "ABC" "DEF"

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

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

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

Ответ 4

Некоторые хорошие ответы здесь, но я хотел бы указать еще пару вещей. Функциональные параметры на самом деле являются местом, где сияет 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

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

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

Теперь функция либо примет имя, либо 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. Это занимает блок script, который должен оцениваться до $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. ($ _ относится к параметру, когда внутри вашего скриптового блока.) Кроме того, вы можете передавать гораздо большие многострочные блоки script, если требуется этот уровень, или использовать несколько скриптовых блоков, подобных мне. Это чрезвычайно полезно и обеспечивает отличные чистые функции и интуитивные исключения.

Ответ 5

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

Test "ABC" "DEF"

Ответ 6

Если вы разработчик С#/Java/С++/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

Ответ 7

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

Код

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
}

Ответ 8

Если вы попытаетесь:

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.

Ответ 9

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

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

Проверьте это.

Ответ 10

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

Test("ABC") ("DEF")

Ответ 11

Поскольку это часто просматриваемый вопрос, я хочу упомянуть, что функция 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"

Вы не используете круглые скобки, как при вызове функции в С#.


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

Ответ 12

Я утверждаю следующее:

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

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

Код:

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

Тест "ABC" "DEF"

Вывод:

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

Ответ 14

Вы можете передать параметры в функцию, как это также.

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

Ответ 15

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

Сплат с массивами (позиционные аргументы)

Тест-соединение с позиционными аргументами

Test-Connection www.google.com localhost

С массивом

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

Обратите внимание, что при разборе мы ссылаемся на переменную со знаком @ вместо @ $. То же самое относится и к использованию Hashtable для сплаттинга.

Splat With Hashtable (именованные аргументы)

Тест-соединение с именованными аргументами

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

С хэштабличным

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

Сплат позиционные и именованные аргументы одновременно

Тест-соединение с позиционными и именованными аргументами

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

Splatting Array и Hashtables вместе

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