it-swarm-ru.tech

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

367
Nasir

Параметры в вызовах функций в 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
494
x0n

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

syntax pitfalls of a function call

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

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

236
Michael Sorens

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

test "ABC" "DEF"

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

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

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

41
Todd

Здесь есть несколько хороших ответов, но я хотел бы отметить пару других вещей. Параметры функций - это место, где светит 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 принимает регулярное выражение, которое гарантирует, что предоставленный параметр соответствует ожидаемому. Если это не так, выдается интуитивно понятное исключение, которое говорит вам, что именно не так. Таким образом, в этом примере "Something" будет работать нормально, но "Summer" не пройдет проверку.

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

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

Test "ABC" "DEF"
13
John B

Если вы являетесь разработчиком 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
10
Ryan Shillington

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

код:

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
}
6
Draino

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

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.

5
RaSor

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

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

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

3
Rodney Fisk

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


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

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
}

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

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

1
Serhii Kimlyk

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

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

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

Код:

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

Тест "Азбука" "DEF"

Результат:

$ var1 value: ABC $ var2 value: DEF

1
Eric

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

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

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

Test-Connection www.google.com localhost

С массивом Splatting

$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
0
Bender the Greatest

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

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