PowerShell'deki bir işleve birden çok parametreyi nasıl iletirim?


438

Birden fazla dize parametresini kabul eden bir işlevim varsa, ilk parametre kendisine atanan tüm verileri alıyor gibi görünüyor ve kalan parametreler boş olarak iletiliyor.

Hızlı bir test komut dosyası:

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

Test("ABC", "DEF")

Üretilen çıktı

$arg1 value: ABC DEF
$arg2 value: 

Doğru çıktı:

$arg1 value: ABC
$arg2 value: DEF

Bu, birden çok makinede v1 ve v2 arasında tutarlı görünüyor, bu yüzden açıkçası yanlış bir şey yapıyorum. Herkes tam olarak neye işaret edebilir mi?


3
Sadece şöyle çağırırsınız:Test "ABC" "DEF"
Ranadip Dutta

Yanıtlar:


576

PowerShell'deki işlevlere yapılan çağrılardaki parametreler (tüm sürümler) virgülle ayrılmış değil, boşlukla ayrılmıştır . Ayrıca, parantezler tamamen kullanılamaz ve Set-StrictModeetkinse PowerShell 2.0'da (veya daha yenisi) ayrıştırma hatasına neden olur . Parantez içindeki bağımsız değişkenler yalnızca .NET yöntemlerinde kullanılır.

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

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

20
Sonunda bunu aklımda 'sopa' yardımcı olan en önemli şey son cümledir: "Parantez içinde argümanlar yalnızca .NET yöntemleri kullanılır."
Ashley

1
Parantez ve virgülle ayrılmış kullanmayı tercih ederim .. Bunu powellhell'de yapmak mümkün mü?
sam yi

8
a (1,2,3)Bir işleve geçmek etkili bir şekilde dizi olarak kabul edilir; tek bir argüman. OO yöntemi stili bağımsız değişkenlerini kullanmak istiyorsanız, modülleri kullanın:$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
x0n

4
Powershellbir kabuk dilidir ve kabuk dillerinin boşlukları belirteç ayırıcı olarak kullanması yaygındır. Ben öyle demezdim Powershellburada farklı ediliyor, doğru gibi diğer sistem varsayılan kabukları doğrultusunda var cmd, sh, bashvb
Bender Greatest

270

Doğru cevap zaten sağlandı, ancak bu konu incelikleri anlamak isteyenler için bazı ek ayrıntıları garanti edecek kadar yaygın görünüyor.

Bunu sadece bir yorum olarak eklerdim, ama bir illüstrasyon eklemek istedim - bunu PowerShell işlevleri hakkındaki hızlı referans çizelgemden yırttı. Bu, f işlevinin imzasının f($a, $b, $c):

Bir işlev çağrısının sözdizimi tuzakları

Böylece, boşlukla ayrılmış konum parametreleri veya sıradan bağımsız adlandırılmış parametrelerle bir fonksiyon çağrılabilir. Diğer tuzaklar, virgül, parantez ve beyaz alanın farkında olmanız gerektiğini ortaya koymaktadır .

Daha fazla okumak için Tavşan Deliği Aşağı: PowerShell Boru Hatları, İşlevleri ve Parametrelerinde Bir Çalışma makalemize bakın . Makale, hızlı başvuru / duvar grafiğine de bir bağlantı içermektedir.


4
Her parametreyi çağıran ve ona bir değer atayan daha ayrıntılı sözdizimi ile açıklama gerçekten onu sağlamlaştırdı. Teşekkürler!
ConstantineK

7
Teşekkürler, neyi yanlış yaptığımı anlamam zihinselliği artırıyordu. Sonunda doğru yaptığımda bu davranışın açıklaması için açtım.
BSAFH

1
Bunu yanıt olarak gönderdiğiniz için teşekkür ederiz. Yanlış bir şeyin neden yanlış olduğunu bilmek de önemlidir.
Gavin Ward

4
Bu çok daha iyi bir cevap. Daha yukarı olmalı.
Mark Bertenshaw

53

Burada bazı iyi cevaplar var, ama birkaç başka noktayı daha belirtmek istedim. İşlev parametreleri aslında PowerShell'in parladığı bir yerdir. Örneğin, aşağıdaki gibi gelişmiş işlevlerde adlandırılmış veya konumsal parametreleriniz olabilir:

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

Daha sonra parametre adını belirterek arayabilir veya bunları açıkça tanımladığınız için yalnızca konum parametrelerini kullanabilirsiniz. Yani bunlardan biri işe yarayacaktır:

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

NameParametre adını açıkça kullandığımız için, ilk örnek ikincisi sağlansa bile çalışır . İkinci örnek yine de konuma göre çalışır, bu yüzden Nameilk olması gerekir. Mümkün olduğunda, her zaman iki seçenek de mevcut olacak şekilde konumları tanımlamaya çalışırım.

PowerShell ayrıca parametre kümelerini tanımlama özelliğine sahiptir. Bunu yöntem aşırı yüklemesi yerine kullanır ve yine oldukça yararlıdır:

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

Şimdi işlev bir isim veya kimlik alacak, ancak her ikisini birden almayacak. Bunları konumsal olarak veya ada göre kullanabilirsiniz. Farklı bir tür olduklarından, PowerShell bunu anlayacaktır. Yani bunların hepsi işe yarayacaktı:

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

Çeşitli parametre setlerine ek parametreler de atayabilirsiniz. (Bu oldukça basit bir örnektir.) İşlev içinde, $ PsCmdlet.ParameterSetName özelliğiyle hangi parametre kümesinin kullanıldığını belirleyebilirsiniz. Örneğin:

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

Ardından, ilgili bir yan notta, PowerShell'de de parametre doğrulaması vardır. Bu benim en sevdiğim PowerShell özelliklerinden biri ve işlevlerinizin içindeki kodu çok temiz yapıyor. Kullanabileceğiniz çok sayıda doğrulama vardır. Birkaç örnek:

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

İlk örnekte ValidatePattern, sağlanan parametrenin beklediğinizle eşleştiğini garanti eden normal bir ifadeyi kabul eder. Değilse, tam olarak neyin yanlış olduğunu söyleyen sezgisel bir istisna atılır. Bu örnekte, 'Bir şey' işe yarayacaktır, ancak 'Yaz' onaylamayı geçmeyecektir.

ValidateRange, parametre değerinin bir tamsayı için beklediğiniz aralık arasında olmasını sağlar. Yani 10 ya da 99 işe yarayacaktı, ama 101 istisna fırlatacaktı.

Bir başka yararlı olan, açıkça kabul edilebilir değerler dizisini tanımlamanızı sağlayan ValidateSet'dir. Başka bir şey girilirse, bir istisna atılır. Başkaları da var, ama muhtemelen en faydalı olanı ValidateScript. Bu, $ true olarak değerlendirilmesi gereken bir komut dosyası bloğu alır, bu nedenle gökyüzü sınırdır. Örneğin:

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

Bu örnekte, yalnızca $ Yolunun var olduğu değil, aynı zamanda bir dosya (dizin yerine) olduğunu ve .csv uzantısına sahip olduğunu garanti ediyoruz. ($ _, scriptblock'unuzun içindeyken parametreyi ifade eder.) Bu düzey gerekiyorsa çok daha büyük, çok satırlı komut dosyası blokları da iletebilir veya burada yaptığım gibi birden çok komut dosyası bloğu kullanabilirsiniz. Son derece yararlıdır ve hoş temiz işlevler ve sezgisel istisnalar sağlar.


3
+1 My_Function -NamedParamater "ParamValue"işlev çağrısı stilini göstermek için . Bu, okunabilirlik için daha fazla PS komut dosyası kodunun izlemesi gereken bir modeldir.
Mister_Tom

46

PowerShell işlevlerini parantez olmadan ve virgül ayırıcı olarak kullanmadan çağırırsınız. Kullanmayı deneyin:

test "ABC" "DEF"

PowerShell'de virgül (,) bir dizi operatörüdür;

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

$aÜç değere sahip bir diziye ayarlanır .


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

Test "ABC" "DEF"

11

Bir C # / Java / C ++ / Ruby / Python / Bu Yüzyıldan Gelen Bir Dil Seçimi geliştiricisiyseniz ve işlevinizi virgülle çağırmak istiyorsanız, çünkü her zaman yaptığınız şey budur, o zaman bir şeye ihtiyacınız var bunun gibi:

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

Şimdi ara:

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

ve göreceksin

arg1 = ABC, and arg2 = DEF

Java, C ++, Ruby ve Python bu yüzyıldan (sadece C #) değil, Gregoryen takvimini varsayar (bazıları diğerlerinden daha fazla evrimleşmiş olsa da).
Peter Mortensen

Heh. @PeterMortensen sizin argümanınız "Bu yüzyıldan ya da sondan bir dil seçin" demeliyim? :-)
Ryan Shillington

10

Eğer işleve kaç argüman geçireceğinizi bilmiyorsanız (veya umursamazsanız) gibi çok basit bir yaklaşımı da kullanabilirsiniz;

Kod :

function FunctionName()
{
    Write-Host $args
}

Bu tüm argümanları basar. Örneğin:

FunctionName a b c 1 2 3

Çıktı

a b c 1 2 3

Bunu, birçok farklı (ve isteğe bağlı) parametreye sahip olabilecek harici komutlar kullanan, ancak sözdizimi hataları vb. Hakkında geri bildirim sağlamak için söz konusu komuta dayanan işlevler oluştururken yararlı buluyorum.

İşte başka bir gerçek dünya örneği (kesilen adı hatırlamaktan nefret ettiğim tracert komutuna bir işlev oluşturmak);

Kod :

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

7

Eğer denersen:

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

olsun:

$arg1 value: ABC GHI
$arg2 value: DEF

Böylece parantezlerin parametreleri ayırdığını görüyorsunuz.


Eğer denersen:

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

olsun:

$arg1 value: ABC
$arg2 value: DEF

Şimdi parantezlerin bir miktar yararlılığını bulabilirsiniz - bir boşluk bir sonraki parametre için ayırıcı olmayacak - bunun yerine bir eval fonksiyonunuz var.


4
Parenler parametreleri ayırmaz. Diziyi tanımlarlar.
n0rd

1
Parens bir dizi tanımlamaz, powershell'in bir dizi olarak yorumlayabileceği bir grup tanımlar. Diziler, aşağıdaki @boş diziden önce bir at işaretiyle ( ) tanımlanır , örneğin bu boş dizi @():; İki numaraları ile veya bu dizi: @(1, 2).
VertigoRay

5

Bu sık görüntülenen bir soru olduğundan, bir PowerShell işlevinin onaylanmış fiiller ( işlev adı olarak Fiil-İsim) kullanması gerektiğini belirtmek isterim . Adın fiil kısmı, cmdlet'in gerçekleştirdiği eylemi tanımlar. Adın isim kısmı, eylemin gerçekleştirildiği varlığı tanımlar. Bu kural , gelişmiş PowerShell kullanıcıları için cmdlet'lerinizin kullanımını basitleştirir .

Ayrıca, parametre olup olmadığı gibi şeyler belirtebilir zorunlu ve pozisyon parametresinin:

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

Parametreyi işleve geçirmek için konumunu kullanabilirsiniz :

Test-Script "Hello" "World"

Veya parametre adını belirtirsiniz :

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

Sen parantez kullanmayın yapmanız gibi C # içinde bir işlev çağırdığınızda.


Birden fazla parametre kullanırken her zaman parametre adlarını iletmenizi öneririm , çünkü bu daha okunabilir .


FYI, onaylanmış fiiller listesi bağlantısı artık çalışmıyor, ancak artık burada bulunabilir - docs.microsoft.com/en-us/powershell/developer/cmdlet/…
Keith

@KeithLangmead Teşekkür ederim Keith, cevabımı da güncelledim.
Martin Brandl

1
"Fiil-İsim" hem fiil hem de isimde büyük harf kullanıldı mı? Cevabı daha açık olacak şekilde değiştirebilir misiniz?
Peter Mortensen

@PeterMortensen Bunu söylediğin için teşekkürler. Cevabımı güncelledim.
Martin Brandl

1
Bir Get-Nodecmdlet'i açığa çıkarmayı düşünün . Bizim için Get-Nodedeğil Retrieve-Node, ne Receive-Node, ne de çağırmak zorunda olduğumuz açıktır .....
Martin Brandl

4

İşlevle ne yaptığınızı bilmiyorum, ancak 'param' anahtar kelimesini kullanarak bir göz atın. Parametreleri bir işleve geçirmek için biraz daha güçlü ve daha kullanıcı dostu hale getiriyor. Aşağıda, Microsoft'tan bu konuda aşırı karmaşık bir makaleye bağlantı verilmiştir. Makalenin ortaya çıkması kadar karmaşık değil.

Param Kullanımı

Ayrıca, bu sitedeki bir sorudan bir örnek :

Bunu kontrol et.


Cevap için teşekkürler. Ancak, işlevi çağırırken sorunlar yaşıyordum. Fonksiyonun param ile mi yoksa onsuz olarak mı bildirildiği önemli değildi.
Nasir

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

Test("ABC") ("DEF")

Sonuç olması gerektiği gibi

2

Daha önce aşağıdakileri söyledim:

Ortak sorun $argyanlış olan tekil formu kullanmaktır . Her zaman olduğu gibi çoğul olmalıdır $args.

Sorun o değil. Aslında, $argbaşka bir şey olabilir. Sorun virgül ve parantezlerin kullanılmasıydı.

Ben çalıştı ve çıktı aşağıdaki kodu çalıştırın:

Kod:

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

Test "ABC" "DEF"

Çıktı:

$var1 value: ABC
$var2 value: DEF

4
Teşekkür ederim arkadaşım, ancak, birkaç yıl geç kaldın :-) Buradaki ilk üç cevap sorunu yeterince ele almıştı. Cevaplanmamış bölüme gitmenizi ve bu soruların bazılarını denemenizi önerebilir miyim?
Nasir


1

Parametreleri aşağıdaki gibi bir fonksiyona da aktarabilirsiniz :

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

3
Bu, bir işlev için parametreleri tanımlamaktır, orijinal soru, işlevi çağırdığınızda parametreleri nasıl belirteceğinizle ilgilidir.
Nasir

1

Burada bahsettiğimi görmüyorum, ancak argümanlarınızı uyarmak yararlı bir alternatiftir ve argümanları dinamik olarak (kullanmanın aksine Invoke-Expression) bir komuta dönüştürüyorsanız özellikle yararlı olur . Konumsal bağımsız değişkenler için dizilerle ve adlandırılmış bağımsız değişkenler için karma öğelerle işaretleyebilirsiniz. İşte bazı örnekler:

Dizilerle Splat (Konumsal Bağımsız Değişkenler)

Konumsal Bağımsız Değişkenlerle Test Bağlantısı

Test-Connection www.google.com localhost

Dizi Sıçraması ile

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

Not splatting, biz ile splatted değişkeni referans zaman o @yerine ait $. Splat için bir Hashtable kullanırken de aynıdır.

Hashtable İle Uyarılma (Adlandırılmış Bağımsız Değişkenler)

Adlandırılmış Bağımsız Değişkenlerle Test Bağlantısı

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

Hashtable Splatting ile

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

Konumsal ve İsimli Bağımsız Değişkenleri Aynı Anda Uyarın

Konumsal ve İsimli Bağımsız Değişkenlerle Test Bağlantısı

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

Dizi ve Hashtables Birlikte Yapışmak

$argumentHash = @{
  Count = 1
}
$argumentArray = 'www.google.com', 'localhost'
Test-Connection @argumentHash @argumentArray
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.