Як передати декілька параметрів у функцію в PowerShell?


438

Якщо у мене є функція, яка приймає більше одного параметра рядка, перший параметр, здається, отримує всі призначені йому дані, а інші параметри передаються як порожні.

Швидкий тестовий сценарій:

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


3
Ви просто дзвоните так:Test "ABC" "DEF"
Ранадіп Датта

Відповіді:


576

Параметри викликів до функцій 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

20
Найголовніше, що нарешті допомогло «приклеїти» це в моїй думці, - це останнє речення: «Парентезовані аргументи використовуються лише у методах .NET».
Ешлі

1
Я вважаю за краще використовувати парантез і розділені комами .. чи можна це зробити в панцирі?
Сам Йі

8
@samyi Ні. Передача (1,2,3)функції до функції ефективно трактується як масив; єдиний аргумент. Якщо ви хочете використовувати аргументи стилю методу OO, використовуйте модулі:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n

4
Powershellє мовою оболонки, і для мов оболонок прийнято використовувати пробіли як роздільник токенів. Я б не сказав , Powershellв даний час тут по- іншому, це право відповідно до іншими оболонками системи по замовчуванням , як cmd, sh, bashі т.д.
Bender Найбільший

270

Правильна відповідь вже надана, але це питання видається досить поширеним, щоб гарантувати деякі додаткові деталі для тих, хто хоче зрозуміти тонкощі.

Я б додав це лише як коментар, але хотів включити ілюстрацію - я зірвав це зі свого швидкого довідкового діаграма щодо функцій PowerShell. Це передбачає, що функція f підпису f($a, $b, $c):

Синтаксичні підводні камені функціонального виклику

Таким чином, можна викликати функцію з позиційними параметрами, розділеними пробілом, або незалежними від порядку названими параметрами. Інші підводні камені показують, що вам потрібно бути відомим комами, дужками та пробілом.

Для подальшого читання дивіться мою статтю « Вниз у кролячій дірці»: дослідження про трубопроводи, функції та параметри PowerShell . Стаття містить посилання на швидку довідкову / настінну діаграму.


4
Пояснення з більш детальним синтаксисом, що викликає кожен параметр і присвоює йому значення, дійсно цементує його. Дякую!
КонстантинК

7
Дякую, це змусило мене думати, не розуміючи, що я роблю не так. Коли я нарешті зробив це правильно, я зголосився на пояснення такої поведінки.
BSAFH

1
Дякуємо, що опублікували це як відповідь. Знання, чому щось не так, так само важливо, як і те, що не так.
Гевін Уорд

4
Це набагато краща відповідь. Це має бути далі вгору.
Марк Бертеншо

53

Тут є кілька хороших відповідей, але я хотів зазначити ще пару речей. Параметри функції - це фактично місце, де сяє 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надається другий, оскільки ми явно використовували ім'я параметра. Другий приклад працює, базуючись на позиції, тому 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
    )
}

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


3
+1 для демонстрації My_Function -NamedParamater "ParamValue"стилю виклику функції. Це шаблон, якому слід читати більше коду сценарію PS для читабельності.
Mister_Tom

46

Ви викликаєте функції PowerShell без дужок і без використання коми як роздільника. Спробуйте використовувати:

test "ABC" "DEF"

У PowerShell кома (,) є оператором масиву, наприклад

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

Він встановлює $aмасив з трьома значеннями.


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

Test "ABC" "DEF"

11

Якщо ви розробник 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

Java, C ++, Ruby та Python не з цього століття (лише C #), припускаючи григоріанський календар (хоча деякі розвинулися більше, ніж інші).
Пітер Мортенсен

Хе. @PeterMortensen ваш аргумент полягає в тому, що я повинен сказати "Виберіть мову з цього століття чи з останнього"? :-)
Райан

10

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

Код :

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
}

7

Якщо ви спробуєте:

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.


4
Парені не розділяють параметри. Вони визначають масив.
n0rd

1
Парени не визначають масив, вони визначають групу, яку повна змога може інтерпретувати як масив. Масиви визначаються з символу ( @) перед ведучим Paren, як цей порожній масив: @(); або цей масив з двома числами: @(1, 2).
VertigoRay

5

Оскільки це питання, яке часто переглядають, я хочу зазначити, що функція PowerShell повинна використовувати затверджені дієслова ( Verb-Noun як назва функції). Дієслівна частина імені визначає дію, яку виконує командлет. Іменникова частина імені визначає сутність, над якою виконується дія. Це правило спрощує використання командлетів для досвідчених користувачів 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 #.


Я рекомендую завжди передавати імена параметрів при використанні більше ніж одного параметра, оскільки це є більш читабельним .


FYI, список посилань на затверджені дієслова більше не працює, але тепер їх можна знайти тут - docs.microsoft.com/en-us/powershell/developer/cmdlet/…
Кіт Лангмід

@KeithLangmead Дякую, Кіт, я також оновив свою відповідь.
Мартін Брандл

1
"Дієслово-іменник", як у дієслові, так і в іменнику з великої літери? Можливо, змініть відповідь, щоб бути більш чітким щодо неї?
Пітер Мортенсен

@PeterMortensen Дякую за те, що згадую. Я оновив свою відповідь.
Мартін Брандл

1
ну, подумайте, ви піддаєте Get-Nodeкомандлет. Нам було б зрозуміло, що ми маємо посилатися Get-Nodeні Retrieve-Node, ні Receive-Node, ні .....
Мартін Брандл

4

Я не знаю, що ви робите з цією функцією, але подивіться на використання ключового слова «парам». Він досить потужніший для передачі параметрів у функцію та робить її більш зручною для користувачів. Нижче наводиться посилання на надмірно складну статтю Microsoft про це. Це не так складно, як ця стаття звучить.

Використання парама

Також, ось приклад із запитання на цьому сайті:

Перевір.


Дякую за відповідь. Однак у мене виникли проблеми при виклику функції. Не важливо, чи була функція оголошена з парам або без неї.
Насір

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

Test("ABC") ("DEF")

Результат вийде як слід:

2

Я заявляв про таке раніше:

Поширеною проблемою є використання форми однини $arg, яка є неправильною. Це завжди має бути множиною як $args.

Проблема не в тому. Насправді, $argможе бути будь-що інше. Проблема полягала у використанні кома та дужок.

Я запускаю наступний код, який працював, і виходить наступний результат:

Код:

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

Тест "ABC" "DEF"

Вихід:

$var1 value: ABC
$var2 value: DEF

4
Дякую, мій друг, проте ти запізнився на пару років :-) Три найкращі відповіді тут достатньо вирішили питання. Чи можу я запропонувати перейти до розділу "Без відповіді" та спробувати деякі з цих питань?
Насір


1

Ви також можете передавати параметри у такій функції :

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

3
Це визначало б параметри для функції, початкове питання полягало у тому, як вказати параметри при виклику функції.
Насір

1

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

Бризок з масивами (позиційні аргументи)

Тест-зв’язок з позиційними аргументами

Test-Connection www.google.com localhost

За допомогою масиву бризки

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

Зауважте, що при сплайтування ми посилаємося на змінну pplatted @замість а $. Це те ж саме, коли використовується 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

Структування масиву та хештелів разом

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.