Làm cách nào để truyền nhiều tham số vào một hàm trong PowerShell?


436

Nếu tôi có một hàm chấp nhận nhiều hơn một tham số chuỗi, tham số đầu tiên dường như nhận được tất cả dữ liệu được gán cho nó và các tham số còn lại được truyền vào dưới dạng trống.

Một kịch bản thử nghiệm nhanh:

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

Test("ABC", "DEF")

Đầu ra được tạo ra là

$arg1 value: ABC DEF
$arg2 value: 

Đầu ra chính xác phải là:

$arg1 value: ABC
$arg2 value: DEF

Điều này dường như phù hợp giữa v1 và v2 trên nhiều máy, vì vậy rõ ràng, tôi đang làm gì đó sai. Bất cứ ai có thể chỉ ra chính xác những gì?


2
Bạn chỉ cần gọi như thế này:Test "ABC" "DEF"
Ranadip Dutta

Câu trả lời:


575

Các tham số trong các lệnh gọi đến các chức năng trong PowerShell (tất cả các phiên bản) được phân tách bằng dấu cách, không được phân tách bằng dấu phẩy . Ngoài ra, các dấu ngoặc đơn hoàn toàn không cần thiết và sẽ gây ra lỗi phân tích cú pháp trong PowerShell 2.0 (hoặc mới hơn) nếu Set-StrictModeđang hoạt động. Các đối số được ngoặc chỉ được sử dụng trong các phương thức .NET.

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

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

20
Điều quan trọng nhất cuối cùng đã giúp 'dính' điều này trong tâm trí tôi là câu cuối cùng: "Các đối số được ngoặc chỉ được sử dụng trong Phương thức .NET."
Ashley

1
Tôi thích sử dụng phép phân tách và dấu phẩy được phân tách .. có thể thực hiện việc này trong powershell không?
sam yi

8
@samyi Không. Truyền một (1,2,3)hàm vào được coi là một mảng hiệu quả; một đối số duy nhất. Nếu bạn muốn sử dụng các đối số kiểu phương thức OO, hãy sử dụng các mô-đun:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n

4
Powershelllà ngôn ngữ hệ vỏ và thông thường các ngôn ngữ hệ vỏ sử dụng khoảng trắng làm dấu tách mã thông báo. Tôi sẽ không nói Powershellđược là khác nhau ở đây, nó phải phù hợp với vỏ mặc định hệ thống khác như cmd, sh, bashvv
Bender các Greatest

269

Câu trả lời đúng đã được cung cấp, nhưng vấn đề này dường như đủ phổ biến để đảm bảo một số chi tiết bổ sung cho những người muốn hiểu về sự tinh tế.

Tôi đã có thể thêm điều này như một nhận xét, nhưng tôi muốn đưa vào một minh họa - tôi đã xé nó ra khỏi biểu đồ tham chiếu nhanh của tôi về các chức năng PowerShell. Giả định này chữ ký của hàm f là f($a, $b, $c):

Cú pháp cú pháp của một cuộc gọi chức năng

Do đó, người ta có thể gọi một hàm với các tham số vị trí được phân tách bằng dấu cách hoặc tham số có tên độc lập theo thứ tự . Những cạm bẫy khác tiết lộ rằng bạn cần phải nhận thức được dấu phẩy, dấu ngoặc đơn khoảng trắng.

Để đọc thêm, hãy xem bài viết của tôi về Lỗ thỏ: Một nghiên cứu về Đường ống, Chức năng và Thông số của PowerShell . Bài viết có chứa một liên kết đến biểu đồ tham khảo / tường nhanh chóng là tốt.


4
Lời giải thích với cú pháp dài dòng hơn gọi từng tham số và gán cho nó một giá trị thực sự gắn kết nó. Cảm ơn!
ConstantineK

7
Cảm ơn bạn, nó đã khiến tôi tinh thần không hiểu những gì tôi đã làm sai. Khi cuối cùng tôi đã làm đúng, tôi rất khao khát được giải thích về hành vi này.
BSAFH

1
Cảm ơn đã đăng bài này như một câu trả lời. Biết tại sao một cái gì đó sai cũng quan trọng như những gì sai.
Phường Gavin

4
Đây là một câu trả lời tốt hơn nhiều. Nó sẽ được tiếp tục lên.
Mark Bertenshaw

53

Có một số câu trả lời tốt ở đây, nhưng tôi muốn chỉ ra một vài điều khác. Các tham số chức năng thực sự là nơi PowerShell tỏa sáng. Ví dụ: bạn có thể có các tham số được đặt tên hoặc vị trí trong các hàm nâng cao như vậy:

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

Sau đó, bạn có thể gọi nó bằng cách chỉ định tên tham số hoặc bạn chỉ có thể sử dụng tham số vị trí, vì bạn đã xác định rõ ràng chúng. Vì vậy, một trong hai sẽ hoạt động:

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

Ví dụ đầu tiên hoạt động ngay cả khi Nameđược cung cấp thứ hai, bởi vì chúng tôi sử dụng rõ ràng tên tham số. Ví dụ thứ hai hoạt động dựa trên vị trí, vì vậy Namesẽ cần phải là đầu tiên. Khi có thể, tôi luôn cố gắng xác định vị trí để cả hai tùy chọn đều khả dụng.

PowerShell cũng có khả năng xác định các bộ tham số. Nó sử dụng điều này thay cho phương thức nạp chồng, và một lần nữa khá hữu ích:

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

Bây giờ hàm sẽ lấy tên hoặc id, nhưng không phải cả hai. Bạn có thể sử dụng chúng theo vị trí, hoặc theo tên. Vì chúng là một loại khác nhau, PowerShell sẽ tìm ra nó. Vì vậy, tất cả những thứ này sẽ hoạt động:

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

Bạn cũng có thể gán các tham số bổ sung cho các bộ tham số khác nhau. (Đó là một ví dụ khá cơ bản rõ ràng.) Bên trong hàm, bạn có thể xác định tập tham số nào đã được sử dụng với thuộc tính $ PsCmdlet.ParameterSetName. Ví dụ:

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

Sau đó, trên một ghi chú bên liên quan, cũng có xác thực tham số trong PowerShell. Đây là một trong những tính năng PowerShell yêu thích của tôi và nó làm cho mã bên trong các chức năng của bạn rất sạch sẽ. Có rất nhiều xác nhận bạn có thể sử dụng. Một vài ví dụ là:

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

Trong ví dụ đầu tiên, ValidatePotype chấp nhận một biểu thức chính quy đảm bảo tham số được cung cấp khớp với những gì bạn mong đợi. Nếu không, một ngoại lệ trực quan sẽ được đưa ra, cho bạn biết chính xác những gì sai. Vì vậy, trong ví dụ đó, 'Cái gì đó' sẽ hoạt động tốt, nhưng 'Mùa hè' sẽ không vượt qua được xác nhận.

ValidateRange đảm bảo rằng giá trị tham số nằm trong phạm vi bạn mong đợi cho một số nguyên. Vì vậy, 10 hoặc 99 sẽ hoạt động, nhưng 101 sẽ ném một ngoại lệ.

Một cái hữu ích khác là ValidateSet, cho phép bạn xác định rõ ràng một mảng các giá trị được chấp nhận. Nếu một cái gì đó khác được nhập vào, một ngoại lệ sẽ được ném ra. Cũng có những cái khác, nhưng có lẽ cái hữu ích nhất là ValidateScript. Điều này cần một khối tập lệnh phải ước tính thành $ true, vì vậy bầu trời là giới hạn. Ví dụ:

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

Trong ví dụ này, chúng tôi đảm bảo không chỉ có $ Path tồn tại mà còn là một tệp, (trái ngược với thư mục) và có phần mở rộng .csv. ($ _ đề cập đến tham số, khi bên trong tập lệnh script của bạn.) Bạn cũng có thể chuyển vào các khối tập lệnh nhiều dòng lớn hơn nhiều nếu mức đó là bắt buộc hoặc sử dụng nhiều tập lệnh như tôi đã làm ở đây. Nó cực kỳ hữu ích và làm cho các chức năng sạch đẹp và ngoại lệ trực quan.


3
+1 để thể hiện My_Function -NamedParamater "ParamValue"kiểu gọi hàm. Đây là một mẫu mà nhiều mã script PS nên tuân theo để dễ đọc.
Mister_Tom

45

Bạn gọi các hàm PowerShell mà không có dấu ngoặc đơn và không sử dụng dấu phẩy làm dấu phân cách. Hãy thử sử dụng:

test "ABC" "DEF"

Trong PowerShell, dấu phẩy (,) là toán tử mảng, vd

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

Nó đặt $athành một mảng với ba giá trị.


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

Test "ABC" "DEF"

11

Nếu bạn là nhà phát triển C # / Java / C ++ / Ruby / Python / Pick-A-Language-From-This-Century và bạn muốn gọi chức năng của mình bằng dấu phẩy, vì đó là điều bạn luôn làm, thì bạn cần một cái gì đó như thế này:

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

Gọi ngay:

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

và bạn sẽ thấy

arg1 = ABC, and arg2 = DEF

Java, C ++, Ruby và Python không có từ thế kỷ này (chỉ C #), giả định lịch Gregorian (mặc dù một số đã phát triển hơn so với những người khác).
Peter Mortensen

Heh. @PeterMortensen lập luận của bạn là tôi nên nói "Chọn một ngôn ngữ từ thế kỷ này hoặc ngôn ngữ cuối cùng"? :-)
Ryan Shillington

10

Nếu bạn không biết (hoặc quan tâm) có bao nhiêu đối số bạn sẽ chuyển đến hàm, bạn cũng có thể sử dụng một cách tiếp cận rất đơn giản như;

Mã số :

function FunctionName()
{
    Write-Host $args
}

Điều đó sẽ in ra tất cả các đối số. Ví dụ:

FunctionName a b c 1 2 3

Đầu ra

a b c 1 2 3

Tôi thấy điều này đặc biệt hữu ích khi tạo các hàm sử dụng các lệnh bên ngoài có thể có nhiều tham số (và tùy chọn) khác nhau, nhưng dựa vào lệnh đã nói để cung cấp phản hồi về lỗi cú pháp, v.v.

Đây là một ví dụ thực tế khác (tạo một hàm cho lệnh tracert, mà tôi ghét phải nhớ tên bị cắt cụt);

Mã số :

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

7

Nếu bạn cố gắng:

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

bạn lấy:

$arg1 value: ABC GHI
$arg2 value: DEF

Vì vậy, bạn thấy rằng dấu ngoặc đơn phân tách các tham số


Nếu bạn cố gắng:

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

bạn lấy:

$arg1 value: ABC
$arg2 value: DEF

Bây giờ bạn có thể tìm thấy một số hữu ích ngay lập tức của dấu ngoặc đơn - một khoảng trắng sẽ không trở thành dấu phân cách cho tham số tiếp theo - thay vào đó bạn có một hàm eval.


4
Parens không tách các tham số. Họ định nghĩa mảng.
n0

1
Parens không định nghĩa một mảng, họ định nghĩa một nhóm, mà powershell có thể hiểu là một mảng. Mảng được định nghĩa với dấu tại ( @) trước paren hàng đầu, như mảng trống này : @(); hoặc mảng này có hai số : @(1, 2).
VertigoRay

5

Vì đây là câu hỏi được xem thường xuyên, tôi muốn đề cập rằng hàm PowerShell nên sử dụng các động từ được phê duyệt ( Động từ-Danh từ làm tên hàm). Phần động từ của tên xác định hành động mà lệnh ghép ngắn thực hiện. Phần danh từ của tên xác định thực thể mà hành động được thực hiện. Quy tắc này đơn giản hóa việc sử dụng các lệnh ghép ngắn của bạn cho người dùng PowerShell nâng cao.

Ngoài ra, bạn có thể chỉ định những thứ như tham số có bắt buộc khôngvị trí của tham số:

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

Để truyền tham số cho hàm, bạn có thể sử dụng vị trí :

Test-Script "Hello" "World"

Hoặc bạn chỉ định tên tham số :

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

Bạn không sử dụng dấu ngoặc đơn như bạn làm khi bạn gọi một hàm trong C #.


Tôi khuyên bạn nên luôn luôn vượt qua các tên tham số khi sử dụng nhiều hơn một tham số, vì điều này dễ đọc hơn .


FYI, liên kết danh sách động từ được phê duyệt không còn hoạt động, nhưng bây giờ có thể được tìm thấy ở đây - docs.microsoft.com/en-us/powershell/developer/cmdlet/ trộm
Keith Langmead

@KeithLangmead Cảm ơn Keith, tôi cũng đã cập nhật câu trả lời của mình.
Martin Brandl

1
"Động từ-danh từ" như trong cả động từ và danh từ viết hoa? Có lẽ thay đổi câu trả lời để rõ ràng hơn về nó?
Peter Mortensen

@PeterMortensen Cảm ơn bạn đã đề cập đến điều đó. Tôi cập nhật câu trả lời của tôi.
Martin Brandl

1
tốt, xem xét bạn để lộ một Get-Nodecmdlet. Rõ ràng là chúng ta phải gọi Get-Node, không Retrieve-Node, cũng không Receive-Node, cũng không .....
Martin Brandl

4

Tôi không biết bạn đang làm gì với chức năng này, nhưng hãy xem sử dụng từ khóa 'param'. Nó mạnh hơn một chút để truyền tham số vào hàm và làm cho nó thân thiện hơn với người dùng. Dưới đây là một liên kết đến một bài viết quá phức tạp từ Microsoft về nó. Nó không phức tạp như bài báo làm cho nó âm thanh.

Sử dụng tham số

Ngoài ra, đây là một ví dụ từ một câu hỏi trên trang web này:

Kiểm tra nó ra.


Cảm ơn câu trả lời. Tuy nhiên, tôi đã gặp vấn đề khi gọi hàm. Không quan trọng nếu hàm được khai báo với param hoặc không có nó.
Nasir

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

Test("ABC") ("DEF")

Kết quả sẽ được đưa ra như mong muốn:

2

Tôi đã nói như sau:

Vấn đề phổ biến là sử dụng hình thức số ít $arg, không chính xác. Nó phải luôn luôn là số nhiều như $args.

Vấn đề không phải là điều đó. Trong thực tế, $argcó thể là bất cứ điều gì khác. Vấn đề là việc sử dụng dấu phẩy và dấu ngoặc đơn.

Tôi chạy mã sau đã hoạt động và đầu ra sau:

Mã số:

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

Kiểm tra "ABC" "DEF"

Đầu ra:

$var1 value: ABC
$var2 value: DEF

4
Cảm ơn bạn của tôi, tuy nhiên, bạn đã trễ vài năm :-) Ba câu trả lời hàng đầu ở đây đã giải quyết đủ vấn đề. Tôi có thể đề nghị đi đến phần Chưa trả lời và thử một số câu hỏi không?
Nasir

2
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

Đây là một paramstuyên bố thích hợp .

Xem liên lạc .

Và nó thực sự hoạt động.


1

Bạn cũng có thể truyền tham số trong một hàm như thế này:

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

3
Đó sẽ là xác định tham số cho hàm, câu hỏi ban đầu là về cách chỉ định tham số khi bạn gọi hàm.
Nasir

1

Tôi không thấy nó được đề cập ở đây, nhưng việc ghép các đối số của bạn là một thay thế hữu ích và trở nên đặc biệt hữu ích nếu bạn đang xây dựng các đối số cho một lệnh một cách linh hoạt (trái ngược với việc sử dụngInvoke-Expression ). Bạn có thể ghép với các mảng cho các đối số vị trí và hàm băm cho các đối số được đặt tên. Dưới đây là một số ví dụ:

Splat With Mảng (Đối số vị trí)

Kiểm tra kết nối với các đối số vị trí

Test-Connection www.google.com localhost

Với mảng Splatter

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

Lưu ý rằng khi ghép hình, chúng ta tham chiếu biến được tách với một @thay vì a $. Điều này cũng tương tự khi sử dụng Hashtable để splat.

Splat With Hashtable (Đối số được đặt tên)

Kiểm tra kết nối với các đối số được đặt tên

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

Với tính năng bẻ khóa Hashtable

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

Splat vị trí và lập luận được đặt tên đồng thời

Kiểm tra kết nối với cả hai đối số vị trí và được đặt tên

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

Splatter Array và Hashtables cùng nhau

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.