PowerShellの関数に複数のパラメーターを渡すにはどうすればよいですか?


436

複数の文字列パラメーターを受け入れる関数がある場合、最初のパラメーターはそれに割り当てられているすべてのデータを取得しているように見え、残りのパラメーターは空として渡されます。

簡単なテストスクリプト:

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の間で一貫しているように見えるので、明らかに、何か間違っています。誰かが正確に何を指摘できますか?


2
あなたは次のように電話するだけです:Test "ABC" "DEF"
Ranadip Dutta

回答:


575

PowerShell(すべてのバージョン)の関数呼び出しのパラメーターは、コンマ区切りではなくスペース区切りです。また、括弧は完全に不要であり、Set-StrictModeアクティブな場合、PowerShell 2.0以降で解析エラーが発生します。括弧で囲まれた引数は、.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
私は括弧とコンマ区切りを使用することを好みます。これをPowerShellで実行することは可能ですか?
sam yi

8
@samyiいいえ。(1,2,3)関数にa を渡すと、配列として効果的に処理されます。単一の引数。:あなたはOOメソッドのスタイルの引数、使用モジュールを使用する場合$m = new-module -ascustomobject { function Add($x,$y) { $x + $y } }; $m.Add(1,1)
X0N

4
Powershellはシェル言語であり、トークン区切り文字としてスペースを使用することは一般的です。私は言わないだろうPowershell、ここでは異なるされており、それは右のような他のシステムのデフォルトのシェルに沿ってだcmdshbash、など
ベンダー最大の

269

正解はすでに提供されていますが、この問題は、微妙な点を理解したい人にいくつかの追加の詳細を保証するのに十分普及しているようです。

これをコメントとして追加したのですが、イラストを含めたかったのですが、PowerShell関数のクイックリファレンスチャートからこれを取り除きました。これは、関数fのシグネチャがf($a, $b, $c)次のことを前提としています。

関数呼び出しの構文上の落とし穴

したがって、スペースで区切られた位置パラメータまたは順序に依存しない名前付きパラメータを使用して関数を呼び出すことができます。他の落とし穴は、コンマ、括弧、および空白を。

詳細については、私の記事「うさぎの穴の穴:PowerShellパイプライン、関数、およびパラメーターの研究」を参照してください。記事には、クイックリファレンス/ウォールチャートへのリンクも含まれています。


4
各パラメーターを呼び出して値を割り当てるより冗長な構文を使用した説明は、非常に固まったものです。ありがとうございます。
ConstantineK

7
ありがとう、それは私が間違っていることを理解していないことを私に精神的にさせていました。ようやく正しくできたとき、私はこの振る舞いの説明を求めていました。
BSAFH 2014

1
これを回答として投稿していただきありがとうございます。なぜ何かがおかしいのかを知ることは、何がおかしいのと同じくらい重要です。
ギャビンワード

4
これははるかに良い答えです。さらにアップする必要があります。
Mark Bertenshaw

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パラメータ名を明示的に使用したため、最初の例は2番目に提供されている場合でも機能します。2番目の例は位置に基づいて機能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
    )
}

これで、関数は名前または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機能の1つであり、関数内のコードを非常にクリーンにします。使用できる検証は多数あります。例をいくつか示します。

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拡張子が付いていることが保証されています。($ _は、スクリプトブロック内のパラメーターを指します。)そのレベルが必要な場合は、より大きな複数行のスクリプトブロックを渡すか、ここで行ったように複数のスクリプトブロックを使用することもできます。これは非常に便利で、すっきりとした機能と直感的な例外をもたらします。


3
+1は、My_Function -NamedParamater "ParamValue"関数呼び出しスタイルを示します。これは、読みやすくするために、より多くのPSスクリプトコードが従うべきパターンです。
Mister_Tom

45

かっこなしで、コンマを区切り記号として使用せずにPowerShell関数を呼び出します。使ってみてください:

test "ABC" "DEF"

PowerShellでは、コンマ(、)は配列演算子です。たとえば、

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

$a3つの値を持つ配列に設定します。


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#のみ)、グレゴリオ暦を想定しています(ただし、他のものよりも進化したものもあります)。
Peter Mortensen

へえ。@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
括弧はパラメータを分離しません。それらは配列を定義します。
2013年

1
括弧は配列を定義しません。Powershellが配列として解釈できるグループを定義します。配列は、次の@空の配列のように、先頭の括弧の前にアットマーク()を付けて定義され@()ます。または、2つの数値を持つこの配列:@(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/...
キースLangmeadを

@KeithLangmeadありがとうキース、私も私の答えを更新しました。
Martin Brandl 2018

1
動詞と名詞の両方が大文字になっている「動詞-名詞」?多分それについてより明確になるように答えを変えますか?
Peter Mortensen

@PeterMortensen言及していただきありがとうございます。回答を更新しました。
Martin Brandl

1
Get-Nodeコマンドレットを公​​開することを検討してください。それは我々が起動していることを私たちのために明らかになりGet-Node、ないRetrieve-Node、もReceive-Node、も...
マーティン・ブランドル

4

関数で何をしているのかはわかりませんが、「param」キーワードの使用方法を確認してください。これは、関数にパラメーターを渡すのにかなり強力で、ユーザーにとって使いやすくなっています。以下は、Microsoftからの非常に複雑な記事へのリンクです。それは記事がそれを鳴らすほど複雑ではありません。

パラメータの使用法

また、これは質問の例ですこのサイトのます。

見てみな。


答えてくれてありがとう。しかし、関数を呼び出すときに問題がありました。関数がparam付きで宣言されたか、それなしで宣言されたかは関係ありませんでした。
Nasir

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
私の友人に感謝します、しかし、あなたは数年遅れています:-)ここでのトップ3の答えは問題を十分に解決しました。未回答セクションに進んで、これらの質問のいくつかを試すことをお勧めできますか?
Nasir 2013年


1

次のような関数でパラメーターを渡すこともできます

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

3
それは関数のパラメーターを定義することになります。元の質問は、関数を呼び出すときにパラメーターを指定する方法に関するものでした。
Nasir

1

ここでは言及していませんが、引数をスプラッティングすることは便利な代替手段であり、コマンドの引数を動的に構築する場合(特に、Invoke-Expression)。位置引数の配列と名前付き引数のハッシュテーブルでスプラットできます。ここではいくつかの例を示します。

配列のあるスプラット(位置引数)

位置引数によるテスト接続

Test-Connection www.google.com localhost

アレイスプラッティング

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

スプラッティングするとき、の@代わりにsplatted変数を参照することに注意してください$。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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.