Bagaimana cara mengirim beberapa parameter ke fungsi di PowerShell?

438

Jika saya memiliki fungsi yang menerima lebih dari satu parameter string, parameter pertama tampaknya mendapatkan semua data yang ditugaskan padanya, dan parameter yang tersisa dilewatkan sebagai kosong.

Skrip pengujian cepat:

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

Test("ABC", "DEF")

Output yang dihasilkan adalah

$arg1 value: ABC DEF
$arg2 value: 

Output yang benar adalah:

$arg1 value: ABC
$arg2 value: DEF

Ini tampaknya konsisten antara v1 dan v2 pada banyak mesin, jadi jelas, saya melakukan sesuatu yang salah. Adakah yang bisa menunjukkan dengan tepat apa?

Nasir
sumber
3
Anda cukup memanggil seperti ini:Test "ABC" "DEF"
Ranadip Dutta

Jawaban:

576

Parameter dalam panggilan ke fungsi di PowerShell (semua versi) dipisahkan oleh ruang, bukan dipisahkan oleh koma . Selain itu, tanda kurung sama sekali tidak perlu dan akan menyebabkan kesalahan parse di PowerShell 2.0 (atau lebih baru) jika Set-StrictModeaktif. Argumen parenthesised digunakan dalam metode .NET saja.

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

ps> foo 1 2 3
a: 1; b: 2; c: 3
x0n
sumber
20
Hal terpenting yang akhirnya membantu 'menempelkan' hal ini di benak saya adalah kalimat terakhir: "Argumen yang dipentesiskan digunakan dalam. Metode NET saja."
Ashley
1
Saya lebih suka menggunakan paranthesis dan dipisahkan koma .. mungkinkah melakukan ini di PowerShell?
sam yi
8
@samyi No. Melewati sebuah (1,2,3)fungsi diperlakukan secara efektif sebagai array; satu argumen. Jika Anda ingin menggunakan argumen gaya metode OO, gunakan modul:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n
4
Powershelladalah bahasa shell dan itu umum bagi bahasa shell untuk menggunakan spasi sebagai pemisah token. Saya tidak akan mengatakan Powershellsedang berbeda di sini, itu benar sejalan dengan kerang default sistem lain seperti cmd, sh, bash, dll
Bender Terbesar
270

Jawaban yang benar telah diberikan, tetapi masalah ini tampaknya cukup lazim untuk menjamin beberapa detail tambahan bagi mereka yang ingin memahami seluk-beluk.

Saya ingin menambahkan ini hanya sebagai komentar, tetapi saya ingin menyertakan ilustrasi - Saya merobeknya dari bagan referensi cepat saya pada fungsi PowerShell. Ini mengasumsikan fungsi tanda tangan f adalah f($a, $b, $c):

Perangkap sintaks dari panggilan fungsi

Dengan demikian, seseorang dapat memanggil fungsi dengan parameter posisi yang dipisah-ruang atau parameter bernama bebas-pesanan . Jebakan lain mengungkapkan bahwa Anda harus menyadari koma, tanda kurung, dan ruang putih.

Untuk bacaan lebih lanjut, lihat artikel saya Down the Rabbit Hole: A Study in PowerShell Pipelines, Functions, and Parameters . Artikel ini juga memuat tautan ke bagan referensi / dinding cepat.

Michael Sorens
sumber
4
Penjelasan dengan sintaksis lebih verbose memanggil setiap parameter dan memberikan nilai yang benar-benar disemen. Terima kasih!
ConstantineK
7
Terima kasih, itu membuat saya mental tidak mengerti apa yang saya lakukan salah. Ketika akhirnya saya melakukannya dengan benar, saya haus akan penjelasan tentang perilaku ini.
BSAFH
1
Terima kasih telah memposting ini sebagai jawaban. Mengetahui mengapa sesuatu yang salah sama pentingnya dengan apa yang salah.
Gavin Ward
4
Ini jawaban yang jauh lebih baik. Itu harus lebih jauh.
Mark Bertenshaw
53

Ada beberapa jawaban bagus di sini, tetapi saya ingin menunjukkan beberapa hal lain. Parameter fungsi sebenarnya adalah tempat PowerShell bersinar. Misalnya, Anda dapat memiliki parameter bernama atau posisional dalam fungsi lanjutan seperti:

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

Kemudian Anda bisa memanggilnya dengan menentukan nama parameter, atau Anda bisa menggunakan parameter posisi, karena Anda secara eksplisit mendefinisikannya. Jadi salah satu dari ini akan berhasil:

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

Contoh pertama berfungsi meskipun Namedisediakan kedua, karena kami secara eksplisit menggunakan nama parameter. Contoh kedua bekerja berdasarkan posisi, jadiName perlu menjadi yang pertama. Jika memungkinkan, saya selalu mencoba menentukan posisi sehingga kedua opsi tersedia.

PowerShell juga memiliki kemampuan untuk mendefinisikan set parameter. Ini menggunakan ini sebagai pengganti metode overloading, dan sekali lagi cukup berguna:

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

Sekarang fungsi akan mengambil nama, atau id, tetapi tidak keduanya. Anda dapat menggunakannya secara posisi, atau dengan nama. Karena mereka adalah tipe yang berbeda, PowerShell akan mencari tahu. Jadi semua ini akan berhasil:

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

Anda juga dapat menetapkan parameter tambahan ke berbagai set parameter. (Itu contoh yang cukup mendasar.) Di dalam fungsi, Anda dapat menentukan set parameter mana yang digunakan dengan properti $ PsCmdlet.ParameterSetName. Sebagai contoh:

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

Kemudian, di sisi catatan terkait, ada juga validasi parameter di PowerShell. Ini adalah salah satu fitur PowerShell favorit saya, dan itu membuat kode di dalam fungsi Anda sangat bersih. Ada banyak validasi yang dapat Anda gunakan. Beberapa contoh adalah:

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

Pada contoh pertama, ValidatePattern menerima ekspresi reguler yang memastikan parameter yang disediakan sesuai dengan yang Anda harapkan. Jika tidak, pengecualian intuitif dilontarkan, memberi tahu Anda apa yang salah. Jadi dalam contoh itu, 'Sesuatu' akan berfungsi dengan baik, tetapi 'Musim Panas' tidak akan lulus validasi.

ValidateRange memastikan bahwa nilai parameter berada di antara rentang yang Anda harapkan untuk bilangan bulat. Jadi 10 atau 99 akan bekerja, tetapi 101 akan mengeluarkan pengecualian.

Satu lagi yang berguna adalah ValidateSet, yang memungkinkan Anda untuk secara eksplisit mendefinisikan array nilai yang dapat diterima. Jika sesuatu dimasukkan, pengecualian akan dilempar. Ada yang lain juga, tapi mungkin yang paling berguna adalah ValidateScript. Ini membutuhkan blok skrip yang harus dievaluasi menjadi $ true, jadi langit adalah batasnya. Sebagai contoh:

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

Dalam contoh ini, kami diyakinkan tidak hanya bahwa $ Path ada, tetapi bahwa itu adalah file, (sebagai lawan dari direktori) dan memiliki ekstensi .csv. ($ _ merujuk pada parameter, ketika di dalam scriptblock Anda.) Anda juga dapat mengirimkan blok skrip multi-baris yang jauh lebih besar jika level itu diperlukan, atau menggunakan beberapa scriptblock seperti yang saya lakukan di sini. Ini sangat berguna dan membuat fungsi bersih yang bagus dan pengecualian intuitif.

pengguna2233949
sumber
3
+1 untuk menunjukkan My_Function -NamedParamater "ParamValue"gaya panggilan fungsi. Ini adalah pola yang harus diikuti lebih banyak kode skrip PS agar mudah dibaca.
Mister_Tom
46

Anda memanggil fungsi PowerShell tanpa tanda kurung dan tanpa menggunakan koma sebagai pemisah. Coba gunakan:

test "ABC" "DEF"

Di PowerShell koma (,) adalah operator array, mis

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

Ini diatur $ake array dengan tiga nilai.

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

Test "ABC" "DEF"
John B
sumber
11

Jika Anda seorang pengembang C # / Java / C ++ / Ruby / Python / Pick-A-Bahasa-Dari-Abad Ini dan Anda ingin memanggil fungsi Anda dengan koma, karena itulah yang selalu Anda lakukan, maka Anda memerlukan sesuatu seperti ini:

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

Sekarang telepon:

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

dan kamu akan lihat

arg1 = ABC, and arg2 = DEF
Ryan Shillington
sumber
Java, C ++, Ruby, dan Python bukan berasal dari abad ini (hanya C #), yang menganggap kalender Gregorian (meskipun beberapa telah berevolusi lebih dari yang lain).
Peter Mortensen
Heh. @PeterMortensen argumen Anda adalah bahwa saya harus mengatakan "Pilih bahasa dari abad ini atau yang terakhir"? :-)
Ryan Shillington
10

Jika Anda tidak tahu (atau peduli) berapa banyak argumen yang akan Anda sampaikan ke fungsi, Anda juga bisa menggunakan pendekatan yang sangat sederhana seperti;

Kode :

function FunctionName()
{
    Write-Host $args
}

Itu akan mencetak semua argumen. Sebagai contoh:

FunctionName a b c 1 2 3

Keluaran

a b c 1 2 3

Saya menemukan ini sangat berguna ketika membuat fungsi yang menggunakan perintah eksternal yang dapat memiliki banyak parameter (dan opsional) yang berbeda, tetapi bergantung pada perintah tersebut untuk memberikan umpan balik pada kesalahan sintaks, dll.

Ini adalah contoh dunia nyata lain (membuat fungsi untuk perintah tracert, yang saya benci harus mengingat nama terpotong);

Kode :

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

Jika kamu mencoba:

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

Anda mendapatkan:

$arg1 value: ABC GHI
$arg2 value: DEF

Jadi Anda melihat bahwa tanda kurung memisahkan parameter


Jika kamu mencoba:

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

Anda mendapatkan:

$arg1 value: ABC
$arg2 value: DEF

Sekarang Anda dapat menemukan kegunaan langsung tanda kurung - spasi tidak akan menjadi pemisah untuk parameter berikutnya - sebagai gantinya Anda memiliki fungsi eval.

RaSor
sumber
4
Orang tua tidak memisahkan parameter. Mereka mendefinisikan array.
n0rd
1
Parens tidak mendefinisikan array, mereka mendefinisikan grup, yang PowerShell dapat menafsirkan sebagai array. Array didefinisikan dengan tanda ( @) sebelum paren terkemuka, seperti array kosong ini: @(); atau array ini dengan dua angka: @(1, 2).
VertigoRay
5

Karena ini adalah pertanyaan yang sering dilihat, saya ingin menyebutkan bahwa fungsi PowerShell harus menggunakan kata kerja yang disetujui ( Verb-Noun sebagai nama fungsi). Bagian kata kerja dari nama mengidentifikasi tindakan yang dilakukan cmdlet. Bagian kata benda dari nama mengidentifikasi entitas tempat tindakan dilakukan. Aturan ini disederhanakan penggunaan cmdlet Anda untuk pengguna PowerShell tingkat lanjut.

Anda juga dapat menentukan hal-hal seperti apakah parameter itu wajib dan posisi parameter:

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

Untuk meneruskan parameter ke fungsi, Anda dapat menggunakan posisi :

Test-Script "Hello" "World"

Atau Anda menentukan nama parameter :

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

Anda tidak menggunakan tanda kurung seperti yang Anda lakukan ketika Anda memanggil fungsi dalam C #.


Saya akan merekomendasikan untuk selalu memberikan nama parameter ketika menggunakan lebih dari satu parameter, karena ini lebih mudah dibaca .

Martin Brandl
sumber
FYI, tautan daftar kata kerja yang disetujui tidak lagi berfungsi, tetapi sekarang dapat ditemukan di sini - docs.microsoft.com/en-us/powershell/developer/cmdlet/…
Keith Langmead
@KeithLangmead Terima kasih Keith, saya memperbarui jawaban saya juga.
Martin Brandl
1
"Verb-Noun" seperti dalam kata kerja dan kata benda dikapitalisasi? Mungkin mengubah jawaban menjadi lebih eksplisit tentang itu?
Peter Mortensen
@PeterMortensen Terima kasih telah menyebutkan itu. Saya memperbarui jawaban saya.
Martin Brandl
1
baik, pertimbangkan Anda mengekspos Get-Nodecmdlet. Jelas bagi kita bahwa kita harus memohon Get-Node, tidak Retrieve-Node, tidak Receive-Node, atau .....
Martin Brandl
4

Saya tidak tahu apa yang Anda lakukan dengan fungsi ini, tetapi coba lihat menggunakan kata kunci 'param'. Ini sedikit lebih kuat untuk melewatkan parameter ke suatu fungsi, dan membuatnya lebih ramah pengguna. Di bawah ini adalah tautan ke artikel yang terlalu rumit dari Microsoft tentang hal itu. Itu tidak serumit artikel membuatnya terdengar.

Penggunaan Param

Juga, ini adalah contoh dari sebuah pertanyaan di situs ini:

Coba lihat.

Rodney Fisk
sumber
Terima kasih atas jawabannya. Namun, saya mengalami masalah saat memanggil fungsi. Tidak masalah jika fungsi tersebut dideklarasikan dengan param atau tanpa itu.
Nasir
3
Function Test([string]$arg1, [string]$arg2)
{
    Write-Host "`$arg1 value: $arg1"
    Write-Host "`$arg2 value: $arg2"
}

Test("ABC") ("DEF")
kleopatra
sumber
Hasilnya akan keluar sebagaimana mestinya:
2

Saya menyatakan yang berikut sebelumnya:

Masalah umum adalah menggunakan bentuk tunggal $arg, yang tidak benar. Itu harus selalu jamak sebagai $args.

Masalahnya bukan itu. Faktanya,$arg bisa jadi apa saja. Masalahnya adalah penggunaan koma dan tanda kurung.

Saya menjalankan kode berikut ini yang berfungsi dan hasilnya mengikuti:

Kode:

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

Tes "ABC" "DEF"

Keluaran:

$var1 value: ABC
$var2 value: DEF
Eric
sumber
4
Terima kasih teman saya, bagaimanapun, Anda terlambat beberapa tahun :-) Tiga jawaban teratas di sini telah cukup mengatasi masalah ini. Bolehkah saya menyarankan menuju ke bagian Belum Dijawab dan mencoba beberapa dari pertanyaan itu?
Nasir
2
Function Test {
    Param([string]$arg1, [string]$arg2)

    Write-Host $arg1
    Write-Host $arg2
}

Ini tepat params deklarasi yang .

Lihat about_Functions_Advanced_Parameters .

Dan itu memang berhasil.

Serhii Kimlyk
sumber
1

Anda dapat melewatkan parameter dalam fungsi seperti ini juga:

function FunctionName()
{
    Param ([string]$ParamName);
    # Operations
}
Kaushal Khamar
sumber
3
Itu akan menjadi parameter untuk suatu fungsi, pertanyaan awal adalah tentang bagaimana menentukan parameter ketika Anda memanggil fungsi.
Nasir
1

Saya tidak melihatnya disebutkan di sini, tetapi menampar argumen Anda adalah alternatif yang berguna dan menjadi sangat berguna jika Anda membangun argumen ke perintah secara dinamis (sebagai lawan menggunakanInvoke-Expression ). Anda dapat memerciki array untuk argumen posisi dan hashtable untuk argumen bernama. Berikut ini beberapa contohnya:

Percikan Dengan Array (Argumen Posisi)

Tes-Koneksi dengan Argumen Posisi

Test-Connection www.google.com localhost

Dengan Array Splatting

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

Perhatikan bahwa ketika melakukan splatting, kami mereferensikan variabel splatted dengan a @bukan $. Ini sama ketika menggunakan Hashtable untuk memerciki juga.

Percik Dengan Hashtable (Argumen Bernama)

Tes-Koneksi dengan Argumen Bernama

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

Dengan Hashtable Splatting

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

Argumen Posisional dan Dinamai Argumen Secara Bersamaan

Tes-Koneksi Dengan Argumen Baik Posisi dan Named

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

Memecah Array dan Hashtable Bersama

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